Support range data types.
Selectivity estimation functions are missing for some range type operators, which is a TODO. Jeff Davis
This commit is contained in:
parent
4334289186
commit
4429f6a9e3
@ -218,6 +218,11 @@
|
|||||||
<entry>functions and procedures</entry>
|
<entry>functions and procedures</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><link linkend="catalog-pg-range"><structname>pg_range</structname></link></entry>
|
||||||
|
<entry>information about range types</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><link linkend="catalog-pg-rewrite"><structname>pg_rewrite</structname></link></entry>
|
<entry><link linkend="catalog-pg-rewrite"><structname>pg_rewrite</structname></link></entry>
|
||||||
<entry>query rewrite rules</entry>
|
<entry>query rewrite rules</entry>
|
||||||
@ -4594,6 +4599,78 @@
|
|||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="catalog-pg-range">
|
||||||
|
<title><structname>pg_range</structname></title>
|
||||||
|
|
||||||
|
<indexterm zone="catalog-pg-range">
|
||||||
|
<primary>pg_range</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The catalog <structname>pg_range</structname> stores information about range types.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<title><structname>pg_range</> Columns</title>
|
||||||
|
|
||||||
|
<tgroup cols="4">
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Name</entry>
|
||||||
|
<entry>Type</entry>
|
||||||
|
<entry>References</entry>
|
||||||
|
<entry>Description</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry><structfield>rngtypid</structfield></entry>
|
||||||
|
<entry><type>oid</type></entry>
|
||||||
|
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
|
||||||
|
<entry>The type that is a range type</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>rngsubtype</structfield></entry>
|
||||||
|
<entry><type>oid</type></entry>
|
||||||
|
<entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
|
||||||
|
<entry>Subtype of this range type, e.g. <type>integer</type> is the subtype of <type>int4range</type></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>rngcollation</structfield></entry>
|
||||||
|
<entry><type>oid</type></entry>
|
||||||
|
<entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry>
|
||||||
|
<entry>The collation used when comparing range boundaries</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>rngsubopc</structfield></entry>
|
||||||
|
<entry><type>oid</type></entry>
|
||||||
|
<entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
|
||||||
|
<entry>The operator class used when comparing range boundaries</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>rngcanonical</structfield></entry>
|
||||||
|
<entry><type>regproc</type></entry>
|
||||||
|
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
|
||||||
|
<entry>A function to convert a range into its canonical form</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>rngsubdiff</structfield></entry>
|
||||||
|
<entry><type>regproc</type></entry>
|
||||||
|
<entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
|
||||||
|
<entry>A function to return the distance between two lower and upper bound, as a <type>double precision</type>. Used for GiST support</entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="catalog-pg-rewrite">
|
<sect1 id="catalog-pg-rewrite">
|
||||||
<title><structname>pg_rewrite</structname></title>
|
<title><structname>pg_rewrite</structname></title>
|
||||||
|
|
||||||
|
@ -4173,6 +4173,8 @@ SET xmloption TO { DOCUMENT | CONTENT };
|
|||||||
|
|
||||||
&rowtypes;
|
&rowtypes;
|
||||||
|
|
||||||
|
&rangetypes;
|
||||||
|
|
||||||
<sect1 id="datatype-oid">
|
<sect1 id="datatype-oid">
|
||||||
<title>Object Identifier Types</title>
|
<title>Object Identifier Types</title>
|
||||||
|
|
||||||
@ -4443,6 +4445,10 @@ SELECT * FROM pg_attribute
|
|||||||
<primary>anyenum</primary>
|
<primary>anyenum</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
|
<indexterm zone="datatype-pseudo">
|
||||||
|
<primary>anyrange</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
<indexterm zone="datatype-pseudo">
|
<indexterm zone="datatype-pseudo">
|
||||||
<primary>void</primary>
|
<primary>void</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
@ -4519,6 +4525,13 @@ SELECT * FROM pg_attribute
|
|||||||
<xref linkend="datatype-enum">).</entry>
|
<xref linkend="datatype-enum">).</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><type>anyrange</></entry>
|
||||||
|
<entry>Indicates that a function accepts any range data type
|
||||||
|
(see <xref linkend="extend-types-polymorphic"> and
|
||||||
|
<xref linkend="rangetypes">).</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><type>anynonarray</></entry>
|
<entry><type>anynonarray</></entry>
|
||||||
<entry>Indicates that a function accepts any non-array data type
|
<entry>Indicates that a function accepts any non-array data type
|
||||||
@ -4583,7 +4596,8 @@ SELECT * FROM pg_attribute
|
|||||||
only <type>void</> and <type>record</> as a result type (plus
|
only <type>void</> and <type>record</> as a result type (plus
|
||||||
<type>trigger</> when the function is used as a trigger). Some also
|
<type>trigger</> when the function is used as a trigger). Some also
|
||||||
support polymorphic functions using the types <type>anyarray</>,
|
support polymorphic functions using the types <type>anyarray</>,
|
||||||
<type>anyelement</>, <type>anyenum</>, and <type>anynonarray</>.
|
<type>anyelement</>, <type>anyenum</>, <type>anyrange</>, and
|
||||||
|
<type>anynonarray</>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -198,14 +198,15 @@
|
|||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Four pseudo-types of special interest are <type>anyelement</>,
|
Five pseudo-types of special interest are <type>anyelement</>,
|
||||||
<type>anyarray</>, <type>anynonarray</>, and <type>anyenum</>,
|
<type>anyarray</>, <type>anynonarray</>, <type>anyenum</>,
|
||||||
which are collectively called <firstterm>polymorphic types</>.
|
and <type>anyrange</>, which are collectively
|
||||||
Any function declared using these types is said to be
|
called <firstterm>polymorphic types</>. Any function declared
|
||||||
a <firstterm>polymorphic function</>. A polymorphic function can
|
using these types is said to be a <firstterm>polymorphic
|
||||||
operate on many different data types, with the specific data type(s)
|
function</>. A polymorphic function can operate on many
|
||||||
being determined by the data types actually passed to it in a particular
|
different data types, with the specific data type(s) being
|
||||||
call.
|
determined by the data types actually passed to it in a
|
||||||
|
particular call.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -221,6 +222,11 @@
|
|||||||
<type>anyelement</type>, the actual array type in the
|
<type>anyelement</type>, the actual array type in the
|
||||||
<type>anyarray</type> positions must be an array whose elements are
|
<type>anyarray</type> positions must be an array whose elements are
|
||||||
the same type appearing in the <type>anyelement</type> positions.
|
the same type appearing in the <type>anyelement</type> positions.
|
||||||
|
Similarly, if there are positions declared <type>anyrange</type>
|
||||||
|
and others declared
|
||||||
|
<type>anyelement</type>, the actual range type in the
|
||||||
|
<type>anyrange</type> positions must be a range whose subtype is
|
||||||
|
the same type appearing in the <type>anyelement</type> positions.
|
||||||
<type>anynonarray</> is treated exactly the same as <type>anyelement</>,
|
<type>anynonarray</> is treated exactly the same as <type>anyelement</>,
|
||||||
but adds the additional constraint that the actual type must not be
|
but adds the additional constraint that the actual type must not be
|
||||||
an array type.
|
an array type.
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
<!ENTITY mvcc SYSTEM "mvcc.sgml">
|
<!ENTITY mvcc SYSTEM "mvcc.sgml">
|
||||||
<!ENTITY perform SYSTEM "perform.sgml">
|
<!ENTITY perform SYSTEM "perform.sgml">
|
||||||
<!ENTITY queries SYSTEM "queries.sgml">
|
<!ENTITY queries SYSTEM "queries.sgml">
|
||||||
|
<!entity rangetypes SYSTEM "rangetypes.sgml">
|
||||||
<!ENTITY rowtypes SYSTEM "rowtypes.sgml">
|
<!ENTITY rowtypes SYSTEM "rowtypes.sgml">
|
||||||
<!ENTITY syntax SYSTEM "syntax.sgml">
|
<!ENTITY syntax SYSTEM "syntax.sgml">
|
||||||
<!ENTITY textsearch SYSTEM "textsearch.sgml">
|
<!ENTITY textsearch SYSTEM "textsearch.sgml">
|
||||||
|
@ -10457,6 +10457,310 @@ SELECT NULLIF(value, '(none)') ...
|
|||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="functions-range">
|
||||||
|
<title>Range Functions and Operators</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<xref linkend="range-operators-table"> shows the operators
|
||||||
|
available for range types.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<table id="range-operators-table">
|
||||||
|
<title>Range Operators</title>
|
||||||
|
<tgroup cols="4">
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Operator</entry>
|
||||||
|
<entry>Description</entry>
|
||||||
|
<entry>Example</entry>
|
||||||
|
<entry>Result</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry> <literal>=</literal> </entry>
|
||||||
|
<entry>equal</entry>
|
||||||
|
<entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal><></literal> </entry>
|
||||||
|
<entry>not equal</entry>
|
||||||
|
<entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal><</literal> </entry>
|
||||||
|
<entry>less than</entry>
|
||||||
|
<entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>></literal> </entry>
|
||||||
|
<entry>greater than</entry>
|
||||||
|
<entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal><=</literal> </entry>
|
||||||
|
<entry>less than or equal</entry>
|
||||||
|
<entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>>=</literal> </entry>
|
||||||
|
<entry>greater than or equal</entry>
|
||||||
|
<entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>@></literal> </entry>
|
||||||
|
<entry>contains</entry>
|
||||||
|
<entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal><@</literal> </entry>
|
||||||
|
<entry>is contained by</entry>
|
||||||
|
<entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>&&</literal> </entry>
|
||||||
|
<entry>overlap (have points in common)</entry>
|
||||||
|
<entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal><<</literal> </entry>
|
||||||
|
<entry>strictly left of</entry>
|
||||||
|
<entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>>></literal> </entry>
|
||||||
|
<entry>strictly right of</entry>
|
||||||
|
<entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>&<</literal> </entry>
|
||||||
|
<entry>Does not extend to the right of?</entry>
|
||||||
|
<entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>&></literal> </entry>
|
||||||
|
<entry>Does not extend to the left of?</entry>
|
||||||
|
<entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>-|-</literal> </entry>
|
||||||
|
<entry>adjacent?</entry>
|
||||||
|
<entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>+</literal> </entry>
|
||||||
|
<entry>Union</entry>
|
||||||
|
<entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
|
||||||
|
<entry><literal>[5,20)</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>-</literal> </entry>
|
||||||
|
<entry>Difference</entry>
|
||||||
|
<entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
|
||||||
|
<entry><literal>[5,10)</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>*</literal> </entry>
|
||||||
|
<entry>Intersection</entry>
|
||||||
|
<entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
|
||||||
|
<entry><literal>[10,15)</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>!?</literal> </entry>
|
||||||
|
<entry>Is empty?</entry>
|
||||||
|
<entry><literal>'empty'::int4range !?</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry> <literal>?</literal> </entry>
|
||||||
|
<entry>Is non-empty?</entry>
|
||||||
|
<entry><literal>numrange(1.0,2.0)?</literal></entry>
|
||||||
|
<entry><literal>t</literal></entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Range comparisons compare the lower bounds first, and only if
|
||||||
|
equal, compare the upper bounds. This is generally most useful for
|
||||||
|
B-tree indexes, rather than being useful comparisons by themselves.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
See <xref linkend="rangetypes"> for more details about range operator
|
||||||
|
behavior.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
<xref linkend="range-functions-table"> shows the functions
|
||||||
|
available for use with range types. See <xref linkend="rangetypes">
|
||||||
|
for more information and examples of the use of these functions.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<indexterm>
|
||||||
|
<primary>lower</primary>
|
||||||
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>upper</primary>
|
||||||
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>empty</primary>
|
||||||
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>non_empty</primary>
|
||||||
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>lower_inc</primary>
|
||||||
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>upper_inc</primary>
|
||||||
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>lower_inf</primary>
|
||||||
|
</indexterm>
|
||||||
|
<indexterm>
|
||||||
|
<primary>upper_inf</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<table id="range-functions-table">
|
||||||
|
<title>Range Functions</title>
|
||||||
|
<tgroup cols="5">
|
||||||
|
<thead>
|
||||||
|
<row>
|
||||||
|
<entry>Function</entry>
|
||||||
|
<entry>Return Type</entry>
|
||||||
|
<entry>Description</entry>
|
||||||
|
<entry>Example</entry>
|
||||||
|
<entry>Result</entry>
|
||||||
|
</row>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal>
|
||||||
|
<function>lower</function>(<type>anyrange</type>)
|
||||||
|
</literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>anyrange</type></entry>
|
||||||
|
<entry>lower bound of range</entry>
|
||||||
|
<entry><literal>lower(numrange(1.1,2.2))</literal></entry>
|
||||||
|
<entry><literal>1.1</literal></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal>
|
||||||
|
<function>upper</function>(<type>anyrange</type>)
|
||||||
|
</literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>anyrange</type></entry>
|
||||||
|
<entry>upper bound of range</entry>
|
||||||
|
<entry><literal>upper(numrange(1.1,2.2))</literal></entry>
|
||||||
|
<entry><literal>2.2</literal></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal>
|
||||||
|
<function>empty</function>(<type>anyrange</type>)
|
||||||
|
</literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>anyrange</type></entry>
|
||||||
|
<entry>is the range empty?</entry>
|
||||||
|
<entry><literal>empty(numrange(1.1,2.2))</literal></entry>
|
||||||
|
<entry><literal>false</literal></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal>
|
||||||
|
<function>non_empty</function>(<type>anyrange</type>)
|
||||||
|
</literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>anyrange</type></entry>
|
||||||
|
<entry>is the range non-empty?</entry>
|
||||||
|
<entry><literal>non_empty(numrange(1.1,2.2))</literal></entry>
|
||||||
|
<entry><literal>true</literal></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal>
|
||||||
|
<function>lower_inc</function>(<type>anyrange</type>)
|
||||||
|
</literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>anyrange</type></entry>
|
||||||
|
<entry>is the lower bound of the range inclusive?</entry>
|
||||||
|
<entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
|
||||||
|
<entry><literal>true</literal></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal>
|
||||||
|
<function>upper_inc</function>(<type>anyrange</type>)
|
||||||
|
</literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>anyrange</type></entry>
|
||||||
|
<entry>is the upper bound of the range inclusive?</entry>
|
||||||
|
<entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
|
||||||
|
<entry><literal>false</literal></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal>
|
||||||
|
<function>lower_inf</function>(<type>anyrange</type>)
|
||||||
|
</literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>anyrange</type></entry>
|
||||||
|
<entry>is the lower bound of the range infinite?</entry>
|
||||||
|
<entry><literal>lower_inf('(,)'::daterange)</literal></entry>
|
||||||
|
<entry><literal>true</literal></entry>
|
||||||
|
</row>
|
||||||
|
<row>
|
||||||
|
<entry>
|
||||||
|
<literal>
|
||||||
|
<function>upper_inf</function>(<type>anyrange</type>)
|
||||||
|
</literal>
|
||||||
|
</entry>
|
||||||
|
<entry><type>anyrange</type></entry>
|
||||||
|
<entry>is the upper bound of the range infinite?</entry>
|
||||||
|
<entry><literal>upper_inf('(,)'::daterange)</literal></entry>
|
||||||
|
<entry><literal>true</literal></entry>
|
||||||
|
</row>
|
||||||
|
</tbody>
|
||||||
|
</tgroup>
|
||||||
|
</table>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="functions-aggregate">
|
<sect1 id="functions-aggregate">
|
||||||
<title>Aggregate Functions</title>
|
<title>Aggregate Functions</title>
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@
|
|||||||
<application>PL/pgSQL</> functions can also be declared to accept
|
<application>PL/pgSQL</> functions can also be declared to accept
|
||||||
and return the polymorphic types
|
and return the polymorphic types
|
||||||
<type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
|
<type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
|
||||||
and <type>anyenum</>. The actual
|
<type>anyenum</>, and <type>anyrange</type>. The actual
|
||||||
data types handled by a polymorphic function can vary from call to
|
data types handled by a polymorphic function can vary from call to
|
||||||
call, as discussed in <xref linkend="extend-types-polymorphic">.
|
call, as discussed in <xref linkend="extend-types-polymorphic">.
|
||||||
An example is shown in <xref linkend="plpgsql-declaration-parameters">.
|
An example is shown in <xref linkend="plpgsql-declaration-parameters">.
|
||||||
@ -500,8 +500,8 @@ $$ LANGUAGE plpgsql;
|
|||||||
<para>
|
<para>
|
||||||
When the return type of a <application>PL/pgSQL</application>
|
When the return type of a <application>PL/pgSQL</application>
|
||||||
function is declared as a polymorphic type (<type>anyelement</type>,
|
function is declared as a polymorphic type (<type>anyelement</type>,
|
||||||
<type>anyarray</type>, <type>anynonarray</type>, or <type>anyenum</>),
|
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
|
||||||
a special parameter <literal>$0</literal>
|
or <type>anyrange</type>), a special parameter <literal>$0</literal>
|
||||||
is created. Its data type is the actual return type of the function,
|
is created. Its data type is the actual return type of the function,
|
||||||
as deduced from the actual input types (see <xref
|
as deduced from the actual input types (see <xref
|
||||||
linkend="extend-types-polymorphic">).
|
linkend="extend-types-polymorphic">).
|
||||||
|
373
doc/src/sgml/rangetypes.sgml
Normal file
373
doc/src/sgml/rangetypes.sgml
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
<!-- doc/src/sgml/rangetypes.sgml -->
|
||||||
|
|
||||||
|
<sect1 id="rangetypes">
|
||||||
|
<title>Range Types</title>
|
||||||
|
|
||||||
|
<indexterm>
|
||||||
|
<primary>range type</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Range types are data types representing a range of values over some
|
||||||
|
sub-type with a total order. For instance, ranges
|
||||||
|
of <type>timestamp</type> might be used to represent the ranges of
|
||||||
|
time that a meeting room is reserved. In this case the data type
|
||||||
|
is <type>tsrange</type> (short for "timestamp range"),
|
||||||
|
and <type>timestamp</type> is the sub-type with a total order.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Range types are useful because they represent many points in a
|
||||||
|
single value. The use of time and date ranges for scheduling
|
||||||
|
purposes is the clearest example; but price ranges, measurement
|
||||||
|
ranges from an instrument, etc., are also useful.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-builtin">
|
||||||
|
<title>Built-in Range Types</title>
|
||||||
|
<para>
|
||||||
|
PostgreSQL comes with the following built-in range types:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<type>INT4RANGE</type> -- Range of <type>INTEGER</type>. This is a discrete range type, see <xref linkend="rangetypes-discrete">.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<type>INT8RANGE</type> -- Range of <type>BIGINT</type>. This is a discrete range type, see <xref linkend="rangetypes-discrete">.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<type>NUMRANGE</type> -- Range of <type>NUMERIC</type>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<type>TSRANGE</type> -- Range of <type>TIMESTAMP WITHOUT TIME ZONE</type>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<type>TSTZRANGE</type> -- Range of <type>TIMESTAMP WITH TIME ZONE</type>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<type>DATERANGE</type> -- Range of <type>DATE</type>. This is a discrete range type, see <xref linkend="rangetypes-discrete">.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
In addition, you can define your own; see <xref linkend="SQL-CREATETYPE"> for more information.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-examples">
|
||||||
|
<title>Examples</title>
|
||||||
|
<para>
|
||||||
|
<programlisting>
|
||||||
|
CREATE TABLE reservation ( during TSRANGE );
|
||||||
|
INSERT INTO reservation VALUES
|
||||||
|
( '[2010-01-01 14:30, 2010-01-01 15:30)' );
|
||||||
|
|
||||||
|
-- Containment
|
||||||
|
SELECT int4range(10, 20) @> 3;
|
||||||
|
|
||||||
|
-- Overlaps
|
||||||
|
SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0);
|
||||||
|
|
||||||
|
-- Find the upper bound:
|
||||||
|
SELECT upper(int8range(15, 25));
|
||||||
|
|
||||||
|
-- Compute the intersection:
|
||||||
|
SELECT int4range(10, 20) * int4range(15, 25);
|
||||||
|
|
||||||
|
-- Is the range non-empty?
|
||||||
|
SELECT numrange(1, 5)? ;
|
||||||
|
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
See <xref linkend="range-functions-table">
|
||||||
|
and <xref linkend="range-operators-table"> for complete lists of
|
||||||
|
functions and operators on range types.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-inclusivity">
|
||||||
|
<title>Inclusive and Exclusive Bounds</title>
|
||||||
|
<para>
|
||||||
|
Every range has two bounds, the lower bound and the upper bound. All
|
||||||
|
points in between those values are included in the range. An
|
||||||
|
inclusive bound means that the boundary point itself is included in
|
||||||
|
the range as well, while an exclusive bound means that the boundary
|
||||||
|
point is not included in the range.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
An inclusive lower bound is represented by <literal>[</literal>
|
||||||
|
while an exclusive lower bound is represented
|
||||||
|
by <literal>(</literal> (see <xref linkend="rangetypes-construct">
|
||||||
|
and <xref linkend="rangetypes-io"> below). Likewise, an inclusive
|
||||||
|
upper bound is represented by <literal>]</literal>, while an
|
||||||
|
exclusive upper bound is represented by <literal>)</literal>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Functions <literal>lower_inc</literal>
|
||||||
|
and <literal>upper_inc</literal> test the inclusivity of the lower
|
||||||
|
and upper bounds of a range, respectively.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-infinite">
|
||||||
|
<title>Infinite (unbounded) Ranges</title>
|
||||||
|
<para>
|
||||||
|
The lower bound of a range can be omitted, meaning that all points
|
||||||
|
less (or equal to, if inclusive) than the upper bound are included
|
||||||
|
in the range. Likewise, if the upper bound of the range is omitted,
|
||||||
|
then all points greater than (or equal to, if omitted) the lower
|
||||||
|
bound are included in the range. If both lower and upper bounds are
|
||||||
|
omitted, all points are considered to be in the range.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Functions <literal>lower_inf</literal>
|
||||||
|
and <literal>upper_inf</literal> test the range for infinite lower
|
||||||
|
and upper bounds of a range, respectively.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-io">
|
||||||
|
<title>Input/Output</title>
|
||||||
|
<para>
|
||||||
|
The input follows one of the following patterns:
|
||||||
|
<synopsis>
|
||||||
|
(<replaceable>lower-bound</replaceable>,<replaceable>upper-bound</replaceable>)
|
||||||
|
(<replaceable>lower-bound</replaceable>,<replaceable>upper-bound</replaceable>]
|
||||||
|
[<replaceable>lower-bound</replaceable>,<replaceable>upper-bound</replaceable>)
|
||||||
|
[<replaceable>lower-bound</replaceable>,<replaceable>upper-bound</replaceable>]
|
||||||
|
empty
|
||||||
|
</synopsis>
|
||||||
|
Notice that the final pattern is <literal>empty</literal>, which
|
||||||
|
represents an empty range (a range that contains no points).
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The <replaceable>lower-bound</replaceable> may be either a string
|
||||||
|
that is valid input for the sub-type, or omitted (to indicate no
|
||||||
|
lower bound); and <replaceable>upper-bound</replaceable> may be
|
||||||
|
either a string that is valid input for the sub-type, or omitted (to
|
||||||
|
indicate no upper bound).
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Either the <replaceable>lower-bound</replaceable> or
|
||||||
|
the <replaceable>upper-bound</replaceable> may be quoted
|
||||||
|
using <literal>""</literal> (double quotation marks), which will allow
|
||||||
|
special characters such as "<literal>,</literal>". Within quotation
|
||||||
|
marks, "<literal>\</literal>" (backslash) serves as an escape
|
||||||
|
character.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The choice between the other input formats affects the inclusivity
|
||||||
|
of the bounds. See <xref linkend="rangetypes-inclusivity">.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Examples:
|
||||||
|
<programlisting>
|
||||||
|
-- includes point 3, does not include point 7, and does include all points in between
|
||||||
|
select '[3,7)'
|
||||||
|
|
||||||
|
-- does not include either 3 or 7, but includes all points in between
|
||||||
|
select '(3,7)'
|
||||||
|
|
||||||
|
-- includes only the single point 4
|
||||||
|
select '[4,4]'
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-construct">
|
||||||
|
<title>Constructing Ranges</title>
|
||||||
|
<para>
|
||||||
|
Each range type has a constructor by the same name. The constructor
|
||||||
|
accepts from zero to three arguments. The zero-argument form
|
||||||
|
constructs an empty range; the one-argument form constructs a
|
||||||
|
singleton range; the two-argument form constructs a range
|
||||||
|
in <literal>[ )</literal> form; and the three-argument form
|
||||||
|
constructs a range in a form specified by the third argument. For
|
||||||
|
example:
|
||||||
|
<programlisting>
|
||||||
|
-- Three-argument form: lower bound, upper bound, and third argument indicating
|
||||||
|
-- inclusivity/exclusivity of bounds (if omitted, defaults to <literal>'[)'</literal>).
|
||||||
|
SELECT numrange(1.0, 14.0, '(]');
|
||||||
|
|
||||||
|
-- The int4range input will exclude the lower bound and include the upper bound; but the
|
||||||
|
-- resulting output will appear in the canonical form; see <xref linkend="rangetypes-discrete">.
|
||||||
|
SELECT int8range(1, 14, '(]');
|
||||||
|
|
||||||
|
-- Single argument form constructs a singleton range; that is a range consisting of just
|
||||||
|
-- one point.
|
||||||
|
SELECT numrange(11.1);
|
||||||
|
|
||||||
|
-- Zero-argument form constructs and empty range.
|
||||||
|
SELECT numrange();
|
||||||
|
|
||||||
|
-- Using NULL for a bound causes the range to be unbounded on that side; that is, negative
|
||||||
|
-- infinity for the lower bound or positive infinity for the upper bound.
|
||||||
|
SELECT numrange(NULL,2.2);
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-discrete">
|
||||||
|
<title>Discrete Range Types</title>
|
||||||
|
<para>
|
||||||
|
Discrete ranges are those that have a
|
||||||
|
defined <literal>canonical</literal> function. Loosely speaking, a
|
||||||
|
discrete range has a sub-type with a well-defined "step";
|
||||||
|
e.g. <type>INTEGER</type> or <type>DATE</type>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The <literal>canonical</literal> function should take an input range
|
||||||
|
value, and return an equal range value that may have a different
|
||||||
|
formatting. For instance, the integer range <literal>[1,
|
||||||
|
7]</literal> could be represented by the equal integer
|
||||||
|
range <literal>[1, 8)</literal>. The two values are equal because
|
||||||
|
there are no points within the integer domain
|
||||||
|
between <literal>7</literal> and <literal>8</literal>, so not
|
||||||
|
including the end point <literal>8</literal> is the same as
|
||||||
|
including the end point <literal>7</literal>. The canonical output
|
||||||
|
for two values that are equal, like <literal>[1, 7]</literal>
|
||||||
|
and <literal>[1, 8)</literal>, must be equal. It doesn't matter
|
||||||
|
which representation you choose to be the canonical one, as long as
|
||||||
|
two equal values with different formattings are always mapped to the
|
||||||
|
same value with the same formatting. If the canonical function is
|
||||||
|
not specified, then ranges with different formatting
|
||||||
|
(e.g. <literal>[1, 7]</literal> and <literal>[1, 8)</literal>) will
|
||||||
|
always be treated as unequal.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For types such as <type>NUMRANGE</type>, this is not possible,
|
||||||
|
because there are always points in between two
|
||||||
|
distinct <type>NUMERIC</type> values.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The built-in range
|
||||||
|
types <type>INT4RANGE</type>, <type>INT8RANGE</type>,
|
||||||
|
and <type>DATERNAGE</type> all use a canonical form that includes
|
||||||
|
the lower bound and excludes the upper bound; that is, <literal>[
|
||||||
|
)</literal>. User-defined ranges can use other conventions, however.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-defining">
|
||||||
|
<title>Defining New Range Types</title>
|
||||||
|
<para>
|
||||||
|
Users can define their own range types. The most common reason to do
|
||||||
|
this is to use ranges where the subtype is not among the built-in
|
||||||
|
range types, e.g. a range of type <type>FLOAT</type> (or, if the
|
||||||
|
subtype itself is a user-defined type).
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
For example: to define a new range type of sub-type <type>DOUBLE PRECISION</type>:
|
||||||
|
<programlisting>
|
||||||
|
CREATE TYPE FLOATRANGE AS RANGE (
|
||||||
|
SUBTYPE = DOUBLE PRECISION
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT '[1.234, 5.678]'::floatrange;
|
||||||
|
</programlisting>
|
||||||
|
Because <type>DOUBLE PRECISION</type> has no meaningful "step", we
|
||||||
|
do not define a <literal>canonical</literal>
|
||||||
|
function. See <xref linkend="SQL-CREATETYPE"> for more
|
||||||
|
information.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Defining your own range type also allows you to specify a different
|
||||||
|
operator class or collation to use (which affects the points that
|
||||||
|
fall between the range boundaries), or a different canonicalization
|
||||||
|
function.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-gist">
|
||||||
|
<indexterm>
|
||||||
|
<primary>range type</primary>
|
||||||
|
<secondary>gist</secondary>
|
||||||
|
</indexterm>
|
||||||
|
<title>Indexing</title>
|
||||||
|
<para>
|
||||||
|
GiST indexes can be applied to a table containing a range type. For instance:
|
||||||
|
<programlisting>
|
||||||
|
CREATE INDEX reservation_idx ON reservation USING gist (during);
|
||||||
|
</programlisting>
|
||||||
|
This index may speed up queries
|
||||||
|
involving <literal>&&</literal>
|
||||||
|
(overlaps), <literal>@></literal> (contains), and all the boolean
|
||||||
|
operators found in this
|
||||||
|
table: <xref linkend="range-operators-table">.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="rangetypes-constraint">
|
||||||
|
<indexterm>
|
||||||
|
<primary>range type</primary>
|
||||||
|
<secondary>exclude</secondary>
|
||||||
|
</indexterm>
|
||||||
|
<title>Constraints on Ranges</title>
|
||||||
|
<para>
|
||||||
|
While <literal>UNIQUE</literal> is a natural constraint for scalar
|
||||||
|
values, it is usually unsuitable for range types. Instead, an
|
||||||
|
exclusion constraint is often more appropriate
|
||||||
|
(see <link linkend="SQL-CREATETABLE-EXCLUDE">CREATE TABLE
|
||||||
|
... CONSTRAINT ... EXCLUDE</link>). Exclusion constraints allow the
|
||||||
|
specification of constraints such as "non-overlapping" on a range
|
||||||
|
type. For example:
|
||||||
|
<programlisting>
|
||||||
|
ALTER TABLE reservation
|
||||||
|
ADD EXCLUDE USING gist (during WITH &&);
|
||||||
|
</programlisting>
|
||||||
|
That constraint will prevent any overlapping values from existing
|
||||||
|
in the table at the same time:
|
||||||
|
<programlisting>
|
||||||
|
INSERT INTO reservation VALUES
|
||||||
|
( '[2010-01-01 11:30, 2010-01-01 13:00)' );
|
||||||
|
-- Result: INSERT 0 1
|
||||||
|
INSERT INTO reservation VALUES
|
||||||
|
( '[2010-01-01 14:45, 2010-01-01 15:45)' );
|
||||||
|
-- Result:
|
||||||
|
-- ERROR: conflicting key value violates exclusion constraint "reservation_during_excl"
|
||||||
|
-- DETAIL: Key (during)=([ 2010-01-01 14:45:00, 2010-01-01 15:45:00 )) conflicts with
|
||||||
|
-- existing key (during)=([ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )).
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Combine range types and exclusion constraints
|
||||||
|
with <link linkend="btree-gist">btree_gist</link> for maximum
|
||||||
|
flexibility defining
|
||||||
|
constraints. After <literal>btree_gist</literal> is installed, the
|
||||||
|
following constraint will prevent overlapping ranges only if the
|
||||||
|
meeting room numbers are equal:
|
||||||
|
<programlisting>
|
||||||
|
|
||||||
|
CREATE TABLE room_reservation
|
||||||
|
(
|
||||||
|
room TEXT,
|
||||||
|
during TSRANGE,
|
||||||
|
EXCLUDE USING gist (room WITH =, during WITH &&)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO room_reservation VALUES
|
||||||
|
( '123A', '[2010-01-01 14:00, 2010-01-01 15:00)' );
|
||||||
|
-- Result: INSERT 0 1
|
||||||
|
INSERT INTO room_reservation VALUES
|
||||||
|
( '123A', '[2010-01-01 14:30, 2010-01-01 15:30)' );
|
||||||
|
-- Result:
|
||||||
|
-- ERROR: conflicting key value violates exclusion constraint "room_reservation_room_during_excl"
|
||||||
|
-- DETAIL: Key (room, during)=(123A, [ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )) conflicts with
|
||||||
|
-- existing key (room, during)=(123A, [ 2010-01-01 14:00:00, 2010-01-01 15:00:00 )).
|
||||||
|
INSERT INTO room_reservation VALUES
|
||||||
|
( '123B', '[2010-01-01 14:30, 2010-01-01 15:30)' );
|
||||||
|
-- Result: INSERT 0 1
|
||||||
|
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
</sect1>
|
@ -27,6 +27,15 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> AS
|
|||||||
CREATE TYPE <replaceable class="parameter">name</replaceable> AS ENUM
|
CREATE TYPE <replaceable class="parameter">name</replaceable> AS ENUM
|
||||||
( [ '<replaceable class="parameter">label</replaceable>' [, ... ] ] )
|
( [ '<replaceable class="parameter">label</replaceable>' [, ... ] ] )
|
||||||
|
|
||||||
|
CREATE TYPE <replaceable class="parameter">name</replaceable> AS RANGE (
|
||||||
|
SUBTYPE = <replaceable class="parameter">subtype</replaceable>,
|
||||||
|
[ , SUBTYPE_OPCLASS = <replaceable class="parameter">subtype_operator_class</replaceable> ]
|
||||||
|
[ , SUBTYPE_DIFF = <replaceable class="parameter">subtype_diff_function</replaceable> ]
|
||||||
|
[ , CANONICAL = <replaceable class="parameter">canonical_function</replaceable> ]
|
||||||
|
[ , ANALYZE = <replaceable class="parameter">analyze_function</replaceable> ]
|
||||||
|
[ , COLLATION = <replaceable class="parameter">collation</replaceable> ]
|
||||||
|
)
|
||||||
|
|
||||||
CREATE TYPE <replaceable class="parameter">name</replaceable> (
|
CREATE TYPE <replaceable class="parameter">name</replaceable> (
|
||||||
INPUT = <replaceable class="parameter">input_function</replaceable>,
|
INPUT = <replaceable class="parameter">input_function</replaceable>,
|
||||||
OUTPUT = <replaceable class="parameter">output_function</replaceable>
|
OUTPUT = <replaceable class="parameter">output_function</replaceable>
|
||||||
@ -98,11 +107,61 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
|
|||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
|
<refsect2 id="SQL-CREATETYPE-RANGE">
|
||||||
|
<title>Range Types</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The third form of <command>CREATE TYPE</command> creates a new
|
||||||
|
range type, as described in <xref linkend="rangetypes">.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <replaceable class="parameter">subtype</replaceable> parameter
|
||||||
|
can be any type with an associated btree opclass (uses the type's
|
||||||
|
default btree operator class unless specified with
|
||||||
|
<replaceable class="parameter">subtype_operator_class</replaceable>).
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <replaceable class="parameter">subtype_diff</replaceable>
|
||||||
|
function takes two values of type
|
||||||
|
<replaceable class="parameter">subtype</replaceable> as argument, and
|
||||||
|
returns the distance between the two values as
|
||||||
|
<type>double precision</type>. This function is used for GiST indexing
|
||||||
|
(see <xref linkend="gist"> for more information), and should be provided
|
||||||
|
for efficiency.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <replaceable class="parameter">canonical</replaceable>
|
||||||
|
function takes an argument and returns a value, both of the same
|
||||||
|
type being defined. This is used to convert the range value to a
|
||||||
|
canonical form, when applicable. See <xref linkend="rangetypes">
|
||||||
|
for more information. To define
|
||||||
|
a <replaceable class="parameter">canonical</replaceable> function,
|
||||||
|
you must first create a <firstterm>shell type</>, which is a
|
||||||
|
placeholder type that has no properties except a name and an
|
||||||
|
owner. This is done by issuing the command <literal>CREATE TYPE
|
||||||
|
<replaceable>name</></literal>, with no additional parameters.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <replaceable class="parameter">analyze</replaceable>
|
||||||
|
function is the same as for creating a base type.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <replaceable class="parameter">collation</replaceable> option
|
||||||
|
specifies the collation used when determining the total order for
|
||||||
|
the range.
|
||||||
|
</para>
|
||||||
|
</refsect2>
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
<title>Base Types</title>
|
<title>Base Types</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The third form of <command>CREATE TYPE</command> creates a new base type
|
The fourth form of <command>CREATE TYPE</command> creates a new base type
|
||||||
(scalar type). To create a new base type, you must be a superuser.
|
(scalar type). To create a new base type, you must be a superuser.
|
||||||
(This restriction is made because an erroneous type definition could
|
(This restriction is made because an erroneous type definition could
|
||||||
confuse or even crash the server.)
|
confuse or even crash the server.)
|
||||||
|
@ -997,8 +997,8 @@ $$ LANGUAGE SQL;
|
|||||||
<para>
|
<para>
|
||||||
<acronym>SQL</acronym> functions can be declared to accept and
|
<acronym>SQL</acronym> functions can be declared to accept and
|
||||||
return the polymorphic types <type>anyelement</type>,
|
return the polymorphic types <type>anyelement</type>,
|
||||||
<type>anyarray</type>, <type>anynonarray</type>, and
|
<type>anyarray</type>, <type>anynonarray</type>,
|
||||||
<type>anyenum</type>. See <xref
|
<type>anyenum</type>, and <type>anyrange</type>. See <xref
|
||||||
linkend="extend-types-polymorphic"> for a more detailed
|
linkend="extend-types-polymorphic"> for a more detailed
|
||||||
explanation of polymorphic functions. Here is a polymorphic
|
explanation of polymorphic functions. Here is a polymorphic
|
||||||
function <function>make_array</function> that builds up an array
|
function <function>make_array</function> that builds up an array
|
||||||
@ -3046,7 +3046,7 @@ CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
|
|||||||
C-language functions can be declared to accept and
|
C-language functions can be declared to accept and
|
||||||
return the polymorphic types
|
return the polymorphic types
|
||||||
<type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
|
<type>anyelement</type>, <type>anyarray</type>, <type>anynonarray</type>,
|
||||||
and <type>anyenum</type>.
|
<type>anyenum</type>, and <type>anyrange</type>.
|
||||||
See <xref linkend="extend-types-polymorphic"> for a more detailed explanation
|
See <xref linkend="extend-types-polymorphic"> for a more detailed explanation
|
||||||
of polymorphic functions. When function arguments or return types
|
of polymorphic functions. When function arguments or return types
|
||||||
are defined as polymorphic types, the function author cannot know
|
are defined as polymorphic types, the function author cannot know
|
||||||
|
@ -13,8 +13,8 @@ include $(top_builddir)/src/Makefile.global
|
|||||||
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
|
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
|
||||||
objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \
|
objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \
|
||||||
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
|
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \
|
||||||
pg_operator.o pg_proc.o pg_db_role_setting.o pg_shdepend.o pg_type.o \
|
pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \
|
||||||
storage.o toasting.o
|
pg_type.o storage.o toasting.o
|
||||||
|
|
||||||
BKIFILES = postgres.bki postgres.description postgres.shdescription
|
BKIFILES = postgres.bki postgres.description postgres.shdescription
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
|
|||||||
pg_ts_parser.h pg_ts_template.h pg_extension.h \
|
pg_ts_parser.h pg_ts_template.h pg_extension.h \
|
||||||
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
|
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
|
||||||
pg_foreign_table.h \
|
pg_foreign_table.h \
|
||||||
pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h \
|
pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \
|
||||||
toasting.h indexing.h \
|
toasting.h indexing.h \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -91,8 +91,11 @@ ProcedureCreate(const char *procedureName,
|
|||||||
int parameterCount;
|
int parameterCount;
|
||||||
int allParamCount;
|
int allParamCount;
|
||||||
Oid *allParams;
|
Oid *allParams;
|
||||||
|
char *modes = NULL;
|
||||||
bool genericInParam = false;
|
bool genericInParam = false;
|
||||||
bool genericOutParam = false;
|
bool genericOutParam = false;
|
||||||
|
bool anyrangeInParam = false;
|
||||||
|
bool anyrangeOutParam = false;
|
||||||
bool internalInParam = false;
|
bool internalInParam = false;
|
||||||
bool internalOutParam = false;
|
bool internalOutParam = false;
|
||||||
Oid variadicType = InvalidOid;
|
Oid variadicType = InvalidOid;
|
||||||
@ -152,6 +155,24 @@ ProcedureCreate(const char *procedureName,
|
|||||||
allParams = parameterTypes->values;
|
allParams = parameterTypes->values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parameterModes != PointerGetDatum(NULL))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We expect the array to be a 1-D CHAR array; verify that. We don't
|
||||||
|
* need to use deconstruct_array() since the array data is just going
|
||||||
|
* to look like a C array of char values.
|
||||||
|
*/
|
||||||
|
ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
|
||||||
|
|
||||||
|
if (ARR_NDIM(modesArray) != 1 ||
|
||||||
|
ARR_DIMS(modesArray)[0] != allParamCount ||
|
||||||
|
ARR_HASNULL(modesArray) ||
|
||||||
|
ARR_ELEMTYPE(modesArray) != CHAROID)
|
||||||
|
elog(ERROR, "parameterModes is not a 1-D char array");
|
||||||
|
modes = (char *) ARR_DATA_PTR(modesArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not allow polymorphic return type unless at least one input argument
|
* Do not allow polymorphic return type unless at least one input argument
|
||||||
* is polymorphic. Also, do not allow return type INTERNAL unless at
|
* is polymorphic. Also, do not allow return type INTERNAL unless at
|
||||||
@ -161,6 +182,9 @@ ProcedureCreate(const char *procedureName,
|
|||||||
{
|
{
|
||||||
switch (parameterTypes->values[i])
|
switch (parameterTypes->values[i])
|
||||||
{
|
{
|
||||||
|
case ANYRANGEOID:
|
||||||
|
anyrangeInParam = true;
|
||||||
|
/* FALL THROUGH */
|
||||||
case ANYARRAYOID:
|
case ANYARRAYOID:
|
||||||
case ANYELEMENTOID:
|
case ANYELEMENTOID:
|
||||||
case ANYNONARRAYOID:
|
case ANYNONARRAYOID:
|
||||||
@ -177,14 +201,17 @@ ProcedureCreate(const char *procedureName,
|
|||||||
{
|
{
|
||||||
for (i = 0; i < allParamCount; i++)
|
for (i = 0; i < allParamCount; i++)
|
||||||
{
|
{
|
||||||
/*
|
if (modes == NULL ||
|
||||||
* We don't bother to distinguish input and output params here, so
|
(modes[i] != PROARGMODE_OUT &&
|
||||||
* if there is, say, just an input INTERNAL param then we will
|
modes[i] != PROARGMODE_INOUT &&
|
||||||
* still set internalOutParam. This is OK since we don't really
|
modes[i] != PROARGMODE_TABLE))
|
||||||
* care.
|
continue;
|
||||||
*/
|
|
||||||
switch (allParams[i])
|
switch (allParams[i])
|
||||||
{
|
{
|
||||||
|
case ANYRANGEOID:
|
||||||
|
anyrangeOutParam = true;
|
||||||
|
/* FALL THROUGH */
|
||||||
case ANYARRAYOID:
|
case ANYARRAYOID:
|
||||||
case ANYELEMENTOID:
|
case ANYELEMENTOID:
|
||||||
case ANYNONARRAYOID:
|
case ANYNONARRAYOID:
|
||||||
@ -205,6 +232,13 @@ ProcedureCreate(const char *procedureName,
|
|||||||
errmsg("cannot determine result data type"),
|
errmsg("cannot determine result data type"),
|
||||||
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
|
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
|
||||||
|
|
||||||
|
if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
|
||||||
|
!anyrangeInParam)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||||
|
errmsg("cannot determine result data type"),
|
||||||
|
errdetail("A function returning ANYRANGE must have at least one ANYRANGE argument.")));
|
||||||
|
|
||||||
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
|
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||||
@ -225,23 +259,8 @@ ProcedureCreate(const char *procedureName,
|
|||||||
procedureName,
|
procedureName,
|
||||||
format_type_be(parameterTypes->values[0]))));
|
format_type_be(parameterTypes->values[0]))));
|
||||||
|
|
||||||
if (parameterModes != PointerGetDatum(NULL))
|
if (modes != NULL)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* We expect the array to be a 1-D CHAR array; verify that. We don't
|
|
||||||
* need to use deconstruct_array() since the array data is just going
|
|
||||||
* to look like a C array of char values.
|
|
||||||
*/
|
|
||||||
ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes);
|
|
||||||
char *modes;
|
|
||||||
|
|
||||||
if (ARR_NDIM(modesArray) != 1 ||
|
|
||||||
ARR_DIMS(modesArray)[0] != allParamCount ||
|
|
||||||
ARR_HASNULL(modesArray) ||
|
|
||||||
ARR_ELEMTYPE(modesArray) != CHAROID)
|
|
||||||
elog(ERROR, "parameterModes is not a 1-D char array");
|
|
||||||
modes = (char *) ARR_DATA_PTR(modesArray);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only the last input parameter can be variadic; if it is, save its
|
* Only the last input parameter can be variadic; if it is, save its
|
||||||
* element type. Errors here are just elog since caller should have
|
* element type. Errors here are just elog since caller should have
|
||||||
|
136
src/backend/catalog/pg_range.c
Normal file
136
src/backend/catalog/pg_range.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* pg_range.c
|
||||||
|
* routines to support manipulation of the pg_range relation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006-2010, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* src/backend/catalog/pg_range.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/genam.h"
|
||||||
|
#include "access/heapam.h"
|
||||||
|
#include "catalog/dependency.h"
|
||||||
|
#include "catalog/indexing.h"
|
||||||
|
#include "catalog/pg_collation.h"
|
||||||
|
#include "catalog/pg_opclass.h"
|
||||||
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "catalog/pg_range.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/tqual.h"
|
||||||
|
#include "utils/rel.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RangeCreate
|
||||||
|
* Create an entry in pg_range.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
|
||||||
|
Oid rangeSubOpclass, RegProcedure rangeCanonical,
|
||||||
|
RegProcedure rangeSubDiff)
|
||||||
|
{
|
||||||
|
Relation pg_range;
|
||||||
|
Datum values[Natts_pg_range];
|
||||||
|
bool nulls[Natts_pg_range];
|
||||||
|
HeapTuple tup;
|
||||||
|
ObjectAddress myself;
|
||||||
|
ObjectAddress referenced;
|
||||||
|
|
||||||
|
pg_range = heap_open(RangeRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
|
memset(nulls, 0, Natts_pg_range * sizeof(bool));
|
||||||
|
|
||||||
|
values[Anum_pg_range_rngtypid - 1] = ObjectIdGetDatum(rangeTypeOid);
|
||||||
|
values[Anum_pg_range_rngsubtype - 1] = ObjectIdGetDatum(rangeSubType);
|
||||||
|
values[Anum_pg_range_rngcollation - 1] = ObjectIdGetDatum(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);
|
||||||
|
|
||||||
|
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
|
||||||
|
simple_heap_insert(pg_range, tup);
|
||||||
|
CatalogUpdateIndexes(pg_range, tup);
|
||||||
|
heap_freetuple(tup);
|
||||||
|
|
||||||
|
/* record dependencies */
|
||||||
|
|
||||||
|
myself.classId = TypeRelationId;
|
||||||
|
myself.objectId = rangeTypeOid;
|
||||||
|
myself.objectSubId = 0;
|
||||||
|
|
||||||
|
referenced.classId = TypeRelationId;
|
||||||
|
referenced.objectId = rangeSubType;
|
||||||
|
referenced.objectSubId = 0;
|
||||||
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
||||||
|
|
||||||
|
referenced.classId = OperatorClassRelationId;
|
||||||
|
referenced.objectId = rangeSubOpclass;
|
||||||
|
referenced.objectSubId = 0;
|
||||||
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
||||||
|
|
||||||
|
if (OidIsValid(rangeCollation))
|
||||||
|
{
|
||||||
|
referenced.classId = CollationRelationId;
|
||||||
|
referenced.objectId = rangeCollation;
|
||||||
|
referenced.objectSubId = 0;
|
||||||
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OidIsValid(rangeCanonical))
|
||||||
|
{
|
||||||
|
referenced.classId = ProcedureRelationId;
|
||||||
|
referenced.objectId = rangeCanonical;
|
||||||
|
referenced.objectSubId = 0;
|
||||||
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OidIsValid(rangeSubDiff))
|
||||||
|
{
|
||||||
|
referenced.classId = ProcedureRelationId;
|
||||||
|
referenced.objectId = rangeSubDiff;
|
||||||
|
referenced.objectSubId = 0;
|
||||||
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_close(pg_range, RowExclusiveLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RangeDelete
|
||||||
|
* Remove the pg_range entry.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
RangeDelete(Oid rangeTypeOid)
|
||||||
|
{
|
||||||
|
Relation pg_range;
|
||||||
|
ScanKeyData key[1];
|
||||||
|
SysScanDesc scan;
|
||||||
|
HeapTuple tup;
|
||||||
|
|
||||||
|
pg_range = heap_open(RangeRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
|
ScanKeyInit(&key[0],
|
||||||
|
Anum_pg_range_rngtypid,
|
||||||
|
BTEqualStrategyNumber, F_OIDEQ,
|
||||||
|
ObjectIdGetDatum(rangeTypeOid));
|
||||||
|
|
||||||
|
scan = systable_beginscan(pg_range, RangeTypidIndexId, true,
|
||||||
|
SnapshotNow, 1, key);
|
||||||
|
|
||||||
|
while (HeapTupleIsValid(tup = systable_getnext(scan)))
|
||||||
|
{
|
||||||
|
simple_heap_delete(pg_range, &tup->t_self);
|
||||||
|
}
|
||||||
|
|
||||||
|
systable_endscan(scan);
|
||||||
|
|
||||||
|
heap_close(pg_range, RowExclusiveLock);
|
||||||
|
}
|
@ -42,7 +42,11 @@
|
|||||||
#include "catalog/pg_constraint.h"
|
#include "catalog/pg_constraint.h"
|
||||||
#include "catalog/pg_depend.h"
|
#include "catalog/pg_depend.h"
|
||||||
#include "catalog/pg_enum.h"
|
#include "catalog/pg_enum.h"
|
||||||
|
#include "catalog/pg_language.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "catalog/pg_proc_fn.h"
|
||||||
|
#include "catalog/pg_range.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "catalog/pg_type_fn.h"
|
#include "catalog/pg_type_fn.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
@ -63,6 +67,7 @@
|
|||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
#include "utils/rangetypes.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
@ -87,6 +92,9 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
|
|||||||
static Oid findTypeTypmodinFunction(List *procname);
|
static Oid findTypeTypmodinFunction(List *procname);
|
||||||
static Oid findTypeTypmodoutFunction(List *procname);
|
static Oid findTypeTypmodoutFunction(List *procname);
|
||||||
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
|
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
|
||||||
|
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
|
||||||
|
static Oid findRangeSubOpclass(List *procname, Oid typeOid);
|
||||||
|
static Oid findRangeSubtypeDiffFunction(List *procname, Oid typeOid);
|
||||||
static void validateDomainConstraint(Oid domainoid, char *ccbin);
|
static void validateDomainConstraint(Oid domainoid, char *ccbin);
|
||||||
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
|
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
|
||||||
static void checkDomainOwner(HeapTuple tup);
|
static void checkDomainOwner(HeapTuple tup);
|
||||||
@ -95,6 +103,8 @@ static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
|
|||||||
Oid baseTypeOid,
|
Oid baseTypeOid,
|
||||||
int typMod, Constraint *constr,
|
int typMod, Constraint *constr,
|
||||||
char *domainName);
|
char *domainName);
|
||||||
|
static void makeRangeConstructor(char *name, Oid namespace, Oid rettype,
|
||||||
|
Oid subtype);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -643,6 +653,14 @@ RemoveTypeById(Oid typeOid)
|
|||||||
if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
|
if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
|
||||||
EnumValuesDelete(typeOid);
|
EnumValuesDelete(typeOid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it is a range type, delete the pg_range entries too; we
|
||||||
|
* don't bother with making dependency entries for those, so it
|
||||||
|
* has to be done "by hand" here.
|
||||||
|
*/
|
||||||
|
if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_RANGE)
|
||||||
|
RangeDelete(typeOid);
|
||||||
|
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
|
|
||||||
heap_close(relation, RowExclusiveLock);
|
heap_close(relation, RowExclusiveLock);
|
||||||
@ -724,14 +742,15 @@ DefineDomain(CreateDomainStmt *stmt)
|
|||||||
basetypeoid = HeapTupleGetOid(typeTup);
|
basetypeoid = HeapTupleGetOid(typeTup);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Base type must be a plain base type, another domain or an enum. Domains
|
* Base type must be a plain base type, another domain, an enum or a range
|
||||||
* over pseudotypes would create a security hole. Domains over composite
|
* type. Domains over pseudotypes would create a security hole. Domains
|
||||||
* types might be made to work in the future, but not today.
|
* over composite types might be made to work in the future, but not today.
|
||||||
*/
|
*/
|
||||||
typtype = baseType->typtype;
|
typtype = baseType->typtype;
|
||||||
if (typtype != TYPTYPE_BASE &&
|
if (typtype != TYPTYPE_BASE &&
|
||||||
typtype != TYPTYPE_DOMAIN &&
|
typtype != TYPTYPE_DOMAIN &&
|
||||||
typtype != TYPTYPE_ENUM)
|
typtype != TYPTYPE_ENUM &&
|
||||||
|
typtype != TYPTYPE_RANGE)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("\"%s\" is not a valid base type for a domain",
|
errmsg("\"%s\" is not a valid base type for a domain",
|
||||||
@ -1134,6 +1153,327 @@ DefineEnum(CreateEnumStmt *stmt)
|
|||||||
pfree(enumArrayName);
|
pfree(enumArrayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DefineRange
|
||||||
|
* Registers a new range type.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
DefineRange(CreateRangeStmt *stmt)
|
||||||
|
{
|
||||||
|
char *typeName;
|
||||||
|
char *rangeArrayName;
|
||||||
|
Oid typeNamespace;
|
||||||
|
Oid typoid;
|
||||||
|
Oid rangeArrayOid;
|
||||||
|
List *parameters = stmt->params;
|
||||||
|
|
||||||
|
ListCell *lc;
|
||||||
|
List *rangeSubOpclassName = NIL;
|
||||||
|
List *rangeSubtypeDiffName = NIL;
|
||||||
|
List *rangeCollationName = NIL;
|
||||||
|
Oid rangeCollation = InvalidOid;
|
||||||
|
regproc rangeAnalyze = InvalidOid;
|
||||||
|
Oid rangeSubtype = InvalidOid;
|
||||||
|
regproc rangeSubOpclass = InvalidOid;
|
||||||
|
regproc rangeCanonical = InvalidOid;
|
||||||
|
regproc rangeSubtypeDiff = InvalidOid;
|
||||||
|
|
||||||
|
AclResult aclresult;
|
||||||
|
|
||||||
|
/* Convert list of names to a name and namespace */
|
||||||
|
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
|
||||||
|
&typeName);
|
||||||
|
|
||||||
|
/* Check we have creation rights in target namespace */
|
||||||
|
aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
|
||||||
|
if (aclresult != ACLCHECK_OK)
|
||||||
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
||||||
|
get_namespace_name(typeNamespace));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look to see if type already exists (presumably as a shell; if not,
|
||||||
|
* TypeCreate will complain).
|
||||||
|
*/
|
||||||
|
typoid = GetSysCacheOid2(TYPENAMENSP,
|
||||||
|
CStringGetDatum(typeName),
|
||||||
|
ObjectIdGetDatum(typeNamespace));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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(typoid) && get_typisdefined(typoid))
|
||||||
|
{
|
||||||
|
if (moveArrayTypeName(typoid, typeName, typeNamespace))
|
||||||
|
typoid = InvalidOid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it doesn't exist, create it as a shell, so that the OID is known for
|
||||||
|
* use in the I/O function definitions.
|
||||||
|
*/
|
||||||
|
if (!OidIsValid(typoid))
|
||||||
|
{
|
||||||
|
typoid = TypeShellMake(typeName, typeNamespace, GetUserId());
|
||||||
|
/* Make new shell type visible for modification below */
|
||||||
|
CommandCounterIncrement();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the command was a parameterless CREATE TYPE, we're done ---
|
||||||
|
* creating the shell type was all we're supposed to do.
|
||||||
|
*/
|
||||||
|
if (parameters == NIL)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Complain if dummy CREATE TYPE and entry already exists */
|
||||||
|
if (parameters == NIL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||||
|
errmsg("type \"%s\" already exists", typeName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(lc, stmt->params)
|
||||||
|
{
|
||||||
|
DefElem *defel = lfirst(lc);
|
||||||
|
|
||||||
|
if (pg_strcasecmp(defel->defname, "subtype") == 0)
|
||||||
|
{
|
||||||
|
if (OidIsValid(rangeSubtype))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("conflicting or redundant options")));
|
||||||
|
rangeSubtype = typenameTypeId(NULL, defGetTypeName(defel));
|
||||||
|
}
|
||||||
|
else if (pg_strcasecmp(defel->defname, "canonical") == 0)
|
||||||
|
{
|
||||||
|
if (OidIsValid(rangeCanonical))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("conflicting or redundant options")));
|
||||||
|
rangeCanonical = findRangeCanonicalFunction(
|
||||||
|
defGetQualifiedName(defel), typoid);
|
||||||
|
}
|
||||||
|
else if (pg_strcasecmp(defel->defname, "collation") == 0)
|
||||||
|
{
|
||||||
|
if (rangeCollationName != NIL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("conflicting or redundant options")));
|
||||||
|
rangeCollationName = defGetQualifiedName(defel);
|
||||||
|
}
|
||||||
|
else if (pg_strcasecmp(defel->defname, "analyze") == 0)
|
||||||
|
{
|
||||||
|
if (OidIsValid(rangeAnalyze))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("conflicting or redundant options")));
|
||||||
|
rangeAnalyze = findTypeAnalyzeFunction(defGetQualifiedName(defel),
|
||||||
|
typoid);
|
||||||
|
}
|
||||||
|
else if (pg_strcasecmp(defel->defname, "subtype_opclass") == 0)
|
||||||
|
{
|
||||||
|
if (rangeSubOpclassName != NIL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("conflicting or redundant options")));
|
||||||
|
rangeSubOpclassName = defGetQualifiedName(defel);
|
||||||
|
}
|
||||||
|
else if (pg_strcasecmp(defel->defname, "subtype_diff") == 0)
|
||||||
|
{
|
||||||
|
if (rangeSubtypeDiffName != NIL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("conflicting or redundant options")));
|
||||||
|
rangeSubtypeDiffName = defGetQualifiedName(defel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("type attribute \"%s\" not recognized",
|
||||||
|
defel->defname)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!OidIsValid(rangeSubtype))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("type attribute \"subtype\" is required")));
|
||||||
|
|
||||||
|
if (type_is_collatable(rangeSubtype))
|
||||||
|
{
|
||||||
|
if (rangeCollationName == NIL)
|
||||||
|
rangeCollation = get_typcollation(rangeSubtype);
|
||||||
|
else
|
||||||
|
rangeCollation = get_collation_oid(rangeCollationName, false);
|
||||||
|
}
|
||||||
|
else if (rangeCollationName != NIL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
|
errmsg("range collation provided but subtype does not support collation")));
|
||||||
|
|
||||||
|
rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
|
||||||
|
|
||||||
|
if (rangeSubtypeDiffName != NIL)
|
||||||
|
rangeSubtypeDiff = findRangeSubtypeDiffFunction(
|
||||||
|
rangeSubtypeDiffName, rangeSubtype);
|
||||||
|
|
||||||
|
rangeArrayOid = AssignTypeArrayOid();
|
||||||
|
|
||||||
|
/* Create the pg_type entry */
|
||||||
|
typoid =
|
||||||
|
TypeCreate(InvalidOid, /* no predetermined type OID */
|
||||||
|
typeName, /* type name */
|
||||||
|
typeNamespace, /* namespace */
|
||||||
|
InvalidOid, /* relation oid (n/a here) */
|
||||||
|
0, /* relation kind (ditto) */
|
||||||
|
GetUserId(), /* owner's ID */
|
||||||
|
-1, /* internal size */
|
||||||
|
TYPTYPE_RANGE, /* type-type (range type) */
|
||||||
|
TYPCATEGORY_RANGE, /* type-category (range type) */
|
||||||
|
false, /* range types are never preferred */
|
||||||
|
DEFAULT_TYPDELIM, /* array element delimiter */
|
||||||
|
F_RANGE_IN, /* input procedure */
|
||||||
|
F_RANGE_OUT, /* output procedure */
|
||||||
|
F_RANGE_RECV, /* receive procedure */
|
||||||
|
F_RANGE_SEND, /* send procedure */
|
||||||
|
InvalidOid, /* typmodin procedure - none */
|
||||||
|
InvalidOid, /* typmodout procedure - none */
|
||||||
|
rangeAnalyze, /* analyze procedure - default */
|
||||||
|
InvalidOid, /* element type ID */
|
||||||
|
false, /* this is not an array type */
|
||||||
|
rangeArrayOid, /* array type we are about to create */
|
||||||
|
InvalidOid, /* base type ID (only for domains) */
|
||||||
|
NULL, /* never a default type value */
|
||||||
|
NULL, /* binary default isn't sent either */
|
||||||
|
false, /* never passed by value */
|
||||||
|
'i', /* int alignment */
|
||||||
|
'x', /* TOAST strategy always plain */
|
||||||
|
-1, /* typMod (Domains only) */
|
||||||
|
0, /* Array dimensions of typbasetype */
|
||||||
|
false, /* Type NOT NULL */
|
||||||
|
InvalidOid); /* typcollation */
|
||||||
|
|
||||||
|
/* create the entry in pg_range */
|
||||||
|
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
|
||||||
|
rangeCanonical, rangeSubtypeDiff);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the array type that goes with it.
|
||||||
|
*/
|
||||||
|
rangeArrayName = makeArrayTypeName(typeName, typeNamespace);
|
||||||
|
|
||||||
|
TypeCreate(rangeArrayOid, /* force assignment of this type OID */
|
||||||
|
rangeArrayName, /* type name */
|
||||||
|
typeNamespace, /* namespace */
|
||||||
|
InvalidOid, /* relation oid (n/a here) */
|
||||||
|
0, /* relation kind (ditto) */
|
||||||
|
GetUserId(), /* owner's ID */
|
||||||
|
-1, /* internal size (always varlena) */
|
||||||
|
TYPTYPE_BASE, /* type-type (base type) */
|
||||||
|
TYPCATEGORY_ARRAY, /* type-category (array) */
|
||||||
|
false, /* array types are never preferred */
|
||||||
|
DEFAULT_TYPDELIM, /* array element delimiter */
|
||||||
|
F_ARRAY_IN, /* input procedure */
|
||||||
|
F_ARRAY_OUT, /* output procedure */
|
||||||
|
F_ARRAY_RECV, /* receive procedure */
|
||||||
|
F_ARRAY_SEND, /* send procedure */
|
||||||
|
InvalidOid, /* typmodin procedure - none */
|
||||||
|
InvalidOid, /* typmodout procedure - none */
|
||||||
|
InvalidOid, /* analyze procedure - default */
|
||||||
|
typoid, /* 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 */
|
||||||
|
'i', /* align 'i' */
|
||||||
|
'x', /* ARRAY is always toastable */
|
||||||
|
-1, /* typMod (Domains only) */
|
||||||
|
0, /* Array dimensions of typbasetype */
|
||||||
|
false, /* Type NOT NULL */
|
||||||
|
InvalidOid); /* typcollation */
|
||||||
|
|
||||||
|
pfree(rangeArrayName);
|
||||||
|
|
||||||
|
makeRangeConstructor(typeName, typeNamespace, typoid, rangeSubtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Because there may exist several range types over one subtype, the range type
|
||||||
|
* can't be determined from the subtype. This means that constructors can't be
|
||||||
|
* polymorphic, and so we must generate a new constructor for every range type
|
||||||
|
* defined.
|
||||||
|
*
|
||||||
|
* We actually define 4 functions with 0 through 3 arguments. This is just to
|
||||||
|
* offer more convenience for the user.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
makeRangeConstructor(char *name, Oid namespace, Oid rangeOid, Oid subtype)
|
||||||
|
{
|
||||||
|
ObjectAddress referenced;
|
||||||
|
Oid constructorArgTypes[3];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
referenced.classId = TypeRelationId;
|
||||||
|
referenced.objectId = rangeOid;
|
||||||
|
referenced.objectSubId = 0;
|
||||||
|
|
||||||
|
constructorArgTypes[0] = subtype;
|
||||||
|
constructorArgTypes[1] = subtype;
|
||||||
|
constructorArgTypes[2] = TEXTOID;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
oidvector *constructorArgTypesVector;
|
||||||
|
ObjectAddress myself;
|
||||||
|
Oid procOid;
|
||||||
|
char *prosrc[4] = { "range_constructor0",
|
||||||
|
"range_constructor1",
|
||||||
|
"range_constructor2",
|
||||||
|
"range_constructor3"};
|
||||||
|
|
||||||
|
constructorArgTypesVector = buildoidvector(constructorArgTypes, i);
|
||||||
|
|
||||||
|
procOid = ProcedureCreate(
|
||||||
|
name, /* name */
|
||||||
|
namespace, /* namespace */
|
||||||
|
false, /* replace */
|
||||||
|
false, /* return set */
|
||||||
|
rangeOid, /* return type */
|
||||||
|
INTERNALlanguageId, /* language */
|
||||||
|
F_FMGR_INTERNAL_VALIDATOR, /* language validator */
|
||||||
|
prosrc[i], /* prosrc */
|
||||||
|
NULL, /* probin */
|
||||||
|
false, /* agg */
|
||||||
|
false, /* window */
|
||||||
|
false, /* security definer */
|
||||||
|
false, /* strict */
|
||||||
|
PROVOLATILE_IMMUTABLE, /* volatility */
|
||||||
|
constructorArgTypesVector, /* param types */
|
||||||
|
PointerGetDatum(NULL), /* allParameterTypes */
|
||||||
|
PointerGetDatum(NULL), /* parameterModes */
|
||||||
|
PointerGetDatum(NULL), /* parameterNames */
|
||||||
|
NIL, /* parameterDefaults */
|
||||||
|
PointerGetDatum(NULL), /* proconfig */
|
||||||
|
1.0, /* procost */
|
||||||
|
0.0); /* prorows */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make the constructor internally-dependent on the range type so that
|
||||||
|
* the user doesn't have to treat them as separate objects.
|
||||||
|
*/
|
||||||
|
myself.classId = ProcedureRelationId;
|
||||||
|
myself.objectId = procOid;
|
||||||
|
myself.objectSubId = 0;
|
||||||
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AlterEnum
|
* AlterEnum
|
||||||
* Adds a new label to an existing enum.
|
* Adds a new label to an existing enum.
|
||||||
@ -1449,6 +1789,103 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
|
|||||||
return procOid;
|
return procOid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find named btree opclass for subtype, or default btree opclass if
|
||||||
|
* opcname is NIL. This will be used for comparing values of subtype.
|
||||||
|
*/
|
||||||
|
static Oid
|
||||||
|
findRangeSubOpclass(List *opcname, Oid subtype)
|
||||||
|
{
|
||||||
|
Oid opcid;
|
||||||
|
|
||||||
|
if (opcname == NIL)
|
||||||
|
{
|
||||||
|
opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
|
||||||
|
if (!OidIsValid(opcid))
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("data type %s has no default operator class for access method \"btree\"",
|
||||||
|
format_type_be(subtype)),
|
||||||
|
errhint("You must specify an operator class for the data type or define a default operator class for the data type.")));
|
||||||
|
}
|
||||||
|
return opcid;
|
||||||
|
}
|
||||||
|
|
||||||
|
opcid = get_opclass_oid(BTREE_AM_OID, opcname, false);
|
||||||
|
|
||||||
|
return opcid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used to find a range's 'canonical' function.
|
||||||
|
*/
|
||||||
|
static Oid
|
||||||
|
findRangeSubtypeDiffFunction(List *procname, Oid typeOid)
|
||||||
|
{
|
||||||
|
Oid argList[2];
|
||||||
|
Oid procOid;
|
||||||
|
|
||||||
|
argList[0] = typeOid;
|
||||||
|
argList[1] = typeOid;
|
||||||
|
|
||||||
|
procOid = LookupFuncName(procname, 2, argList, true);
|
||||||
|
|
||||||
|
if (!OidIsValid(procOid))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
||||||
|
errmsg("function %s does not exist",
|
||||||
|
func_signature_string(procname, 2, NIL, argList))));
|
||||||
|
|
||||||
|
if (get_func_rettype(procOid) != FLOAT8OID)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("range subtype diff function %s must return type \"float8\"",
|
||||||
|
func_signature_string(procname, 2, NIL, argList))));
|
||||||
|
|
||||||
|
if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("range subtype diff function %s must be immutable",
|
||||||
|
func_signature_string(procname, 2, NIL, argList))));
|
||||||
|
|
||||||
|
return procOid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used to find a range's 'canonical' function.
|
||||||
|
*/
|
||||||
|
static Oid
|
||||||
|
findRangeCanonicalFunction(List *procname, Oid typeOid)
|
||||||
|
{
|
||||||
|
Oid argList[1];
|
||||||
|
Oid procOid;
|
||||||
|
|
||||||
|
argList[0] = typeOid;
|
||||||
|
|
||||||
|
procOid = LookupFuncName(procname, 1, argList, true);
|
||||||
|
|
||||||
|
if (!OidIsValid(procOid))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
||||||
|
errmsg("function %s does not exist",
|
||||||
|
func_signature_string(procname, 1, NIL, argList))));
|
||||||
|
|
||||||
|
if (get_func_rettype(procOid) != typeOid)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("range canonical function %s must return range type",
|
||||||
|
NameListToString(procname))));
|
||||||
|
|
||||||
|
if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("range canonical function %s must be immutable",
|
||||||
|
func_signature_string(procname, 1, NIL, argList))));
|
||||||
|
|
||||||
|
return procOid;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AssignTypeArrayOid
|
* AssignTypeArrayOid
|
||||||
*
|
*
|
||||||
|
@ -1352,6 +1352,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
|||||||
if (fn_typtype == TYPTYPE_BASE ||
|
if (fn_typtype == TYPTYPE_BASE ||
|
||||||
fn_typtype == TYPTYPE_DOMAIN ||
|
fn_typtype == TYPTYPE_DOMAIN ||
|
||||||
fn_typtype == TYPTYPE_ENUM ||
|
fn_typtype == TYPTYPE_ENUM ||
|
||||||
|
fn_typtype == TYPTYPE_RANGE ||
|
||||||
rettype == VOIDOID)
|
rettype == VOIDOID)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -3055,6 +3055,17 @@ _copyCreateEnumStmt(CreateEnumStmt *from)
|
|||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CreateRangeStmt *
|
||||||
|
_copyCreateRangeStmt(CreateRangeStmt *from)
|
||||||
|
{
|
||||||
|
CreateRangeStmt *newnode = makeNode(CreateRangeStmt);
|
||||||
|
|
||||||
|
COPY_NODE_FIELD(typeName);
|
||||||
|
COPY_NODE_FIELD(params);
|
||||||
|
|
||||||
|
return newnode;
|
||||||
|
}
|
||||||
|
|
||||||
static AlterEnumStmt *
|
static AlterEnumStmt *
|
||||||
_copyAlterEnumStmt(AlterEnumStmt *from)
|
_copyAlterEnumStmt(AlterEnumStmt *from)
|
||||||
{
|
{
|
||||||
@ -4297,6 +4308,9 @@ copyObject(void *from)
|
|||||||
case T_CreateEnumStmt:
|
case T_CreateEnumStmt:
|
||||||
retval = _copyCreateEnumStmt(from);
|
retval = _copyCreateEnumStmt(from);
|
||||||
break;
|
break;
|
||||||
|
case T_CreateRangeStmt:
|
||||||
|
retval = _copyCreateRangeStmt(from);
|
||||||
|
break;
|
||||||
case T_AlterEnumStmt:
|
case T_AlterEnumStmt:
|
||||||
retval = _copyAlterEnumStmt(from);
|
retval = _copyAlterEnumStmt(from);
|
||||||
break;
|
break;
|
||||||
|
@ -1438,6 +1438,15 @@ _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
_equalCreateRangeStmt(CreateRangeStmt *a, CreateRangeStmt *b)
|
||||||
|
{
|
||||||
|
COMPARE_NODE_FIELD(typeName);
|
||||||
|
COMPARE_NODE_FIELD(params);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
_equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
|
_equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
|
||||||
{
|
{
|
||||||
@ -2826,6 +2835,9 @@ equal(void *a, void *b)
|
|||||||
case T_CreateEnumStmt:
|
case T_CreateEnumStmt:
|
||||||
retval = _equalCreateEnumStmt(a, b);
|
retval = _equalCreateEnumStmt(a, b);
|
||||||
break;
|
break;
|
||||||
|
case T_CreateRangeStmt:
|
||||||
|
retval = _equalCreateRangeStmt(a, b);
|
||||||
|
break;
|
||||||
case T_AlterEnumStmt:
|
case T_AlterEnumStmt:
|
||||||
retval = _equalAlterEnumStmt(a, b);
|
retval = _equalAlterEnumStmt(a, b);
|
||||||
break;
|
break;
|
||||||
|
@ -4336,6 +4336,13 @@ DefineStmt:
|
|||||||
n->vals = $7;
|
n->vals = $7;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
|
| CREATE TYPE_P any_name AS RANGE definition
|
||||||
|
{
|
||||||
|
CreateRangeStmt *n = makeNode(CreateRangeStmt);
|
||||||
|
n->typeName = $3;
|
||||||
|
n->params = $6;
|
||||||
|
$$ = (Node *)n;
|
||||||
|
}
|
||||||
| CREATE TEXT_P SEARCH PARSER any_name definition
|
| CREATE TEXT_P SEARCH PARSER any_name definition
|
||||||
{
|
{
|
||||||
DefineStmt *n = makeNode(DefineStmt);
|
DefineStmt *n = makeNode(DefineStmt);
|
||||||
|
@ -158,7 +158,8 @@ coerce_type(ParseState *pstate, Node *node,
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
if (targetTypeId == ANYARRAYOID ||
|
if (targetTypeId == ANYARRAYOID ||
|
||||||
targetTypeId == ANYENUMOID)
|
targetTypeId == ANYENUMOID ||
|
||||||
|
targetTypeId == ANYRANGEOID)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Assume can_coerce_type verified that implicit coercion is okay.
|
* Assume can_coerce_type verified that implicit coercion is okay.
|
||||||
@ -1275,9 +1276,11 @@ coerce_to_common_type(ParseState *pstate, Node *node,
|
|||||||
* 1) All arguments declared ANYARRAY must have matching datatypes,
|
* 1) All arguments declared ANYARRAY must have matching datatypes,
|
||||||
* and must in fact be varlena arrays.
|
* and must in fact be varlena arrays.
|
||||||
* 2) All arguments declared ANYELEMENT must have matching datatypes.
|
* 2) All arguments declared ANYELEMENT must have matching datatypes.
|
||||||
* 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
|
* 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the
|
||||||
* the actual ANYELEMENT datatype is in fact the element type for
|
* actual ANYELEMENT datatype is in fact the element type for the actual
|
||||||
* the actual ANYARRAY datatype.
|
* ANYARRAY datatype. Similarly, if there are arguments of both ANYELEMENT
|
||||||
|
* and ANYRANGE, make sure the actual ANYELEMENT datatype is in fact the
|
||||||
|
* subtype for the actual ANYRANGE type.
|
||||||
* 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
|
* 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
|
||||||
* (alone or in combination with plain ANYELEMENT), we add the extra
|
* (alone or in combination with plain ANYELEMENT), we add the extra
|
||||||
* condition that the ANYELEMENT type must be an enum.
|
* condition that the ANYELEMENT type must be an enum.
|
||||||
@ -1311,6 +1314,8 @@ check_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
Oid elem_typeid = InvalidOid;
|
Oid elem_typeid = InvalidOid;
|
||||||
Oid array_typeid = InvalidOid;
|
Oid array_typeid = InvalidOid;
|
||||||
Oid array_typelem;
|
Oid array_typelem;
|
||||||
|
Oid range_typeid = InvalidOid;
|
||||||
|
Oid range_typelem;
|
||||||
bool have_anyelement = false;
|
bool have_anyelement = false;
|
||||||
bool have_anynonarray = false;
|
bool have_anynonarray = false;
|
||||||
bool have_anyenum = false;
|
bool have_anyenum = false;
|
||||||
@ -1348,6 +1353,15 @@ check_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
return false;
|
return false;
|
||||||
array_typeid = actual_type;
|
array_typeid = actual_type;
|
||||||
}
|
}
|
||||||
|
else if (decl_type == ANYRANGEOID)
|
||||||
|
{
|
||||||
|
if (actual_type == UNKNOWNOID)
|
||||||
|
continue;
|
||||||
|
actual_type = getBaseType(actual_type);
|
||||||
|
if (OidIsValid(range_typeid) && actual_type != range_typeid)
|
||||||
|
return false;
|
||||||
|
range_typeid = actual_type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the element type based on the array type, if we have one */
|
/* Get the element type based on the array type, if we have one */
|
||||||
@ -1393,6 +1407,27 @@ check_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the element type based on the range type, if we have one */
|
||||||
|
if (OidIsValid(range_typeid))
|
||||||
|
{
|
||||||
|
range_typelem = get_range_subtype(range_typeid);
|
||||||
|
if (!OidIsValid(range_typelem))
|
||||||
|
return false; /* should be a range, but isn't */
|
||||||
|
|
||||||
|
if (!OidIsValid(elem_typeid))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* if we don't have an element type yet, use the one we just got
|
||||||
|
*/
|
||||||
|
elem_typeid = range_typelem;
|
||||||
|
}
|
||||||
|
else if (range_typelem != elem_typeid)
|
||||||
|
{
|
||||||
|
/* otherwise, they better match */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Looks valid */
|
/* Looks valid */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1416,23 +1451,28 @@ check_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
* if it is declared as a polymorphic type:
|
* if it is declared as a polymorphic type:
|
||||||
*
|
*
|
||||||
* 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
|
* 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
|
||||||
* argument's actual type as the function's return type.
|
* argument's actual type as the function's return type. Similarly, if
|
||||||
* 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument
|
* return type is ANYRANGE, and any argument is ANYRANGE, use the argument's
|
||||||
* is ANYELEMENT, use the actual type of the argument to determine
|
* actual type as the function's return type.
|
||||||
* the function's return type, i.e. the element type's corresponding
|
* 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument is
|
||||||
* array type.
|
* ANYELEMENT, use the actual type of the argument to determine the
|
||||||
|
* function's return type, i.e. the element type's corresponding array
|
||||||
|
* type. Note: similar behavior does not exist for ANYRANGE, because it's
|
||||||
|
* impossble to determine the range type from the subtype alone.\
|
||||||
* 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
|
* 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
|
||||||
* generate an ERROR. This condition is prevented by CREATE FUNCTION
|
* generate an ERROR. Similarly, if the return type is ANYRANGE, and no
|
||||||
* and is therefore not expected here.
|
* argument is ANYRANGE or ANYELEMENT, generate an error. These conditions
|
||||||
|
* are prevented by CREATE FUNCTION and is therefore not expected here.
|
||||||
* 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
|
* 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
|
||||||
* argument's actual type as the function's return type.
|
* argument's actual type as the function's return type.
|
||||||
* 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any
|
* 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument
|
||||||
* argument is ANYARRAY, use the actual type of the argument to determine
|
* is ANYARRAY or ANYRANGE, use the actual type of the argument to determine
|
||||||
* the function's return type, i.e. the array type's corresponding
|
* the function's return type; i.e. the array type's corresponding element
|
||||||
* element type.
|
* type or the range type's corresponding subtype (or both, in which case
|
||||||
* 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
|
* they must match).
|
||||||
* generate an ERROR. This condition is prevented by CREATE FUNCTION
|
* 6) If return type is ANYELEMENT, no argument is ANYARRAY, ANYRANGE, or
|
||||||
* and is therefore not expected here.
|
* ANYELEMENT, generate an ERROR. This condition is prevented by CREATE
|
||||||
|
* FUNCTION and is therefore not expected here.
|
||||||
* 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
|
* 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
|
||||||
* (alone or in combination with plain ANYELEMENT), we add the extra
|
* (alone or in combination with plain ANYELEMENT), we add the extra
|
||||||
* condition that the ANYELEMENT type must be an enum.
|
* condition that the ANYELEMENT type must be an enum.
|
||||||
@ -1441,9 +1481,10 @@ check_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
|
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
|
||||||
* is an extra restriction if not.)
|
* is an extra restriction if not.)
|
||||||
*
|
*
|
||||||
* Domains over arrays match ANYARRAY arguments, and are immediately flattened
|
* Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
|
||||||
* to their base type. (In particular, if the return type is also ANYARRAY,
|
* respectively, and are immediately flattened to their base type. (In
|
||||||
* we'll set it to the base type not the domain type.)
|
* particular, if the return type is also ANYARRAY or ANYRANGE, we'll set it to
|
||||||
|
* the base type not the domain type.)
|
||||||
*
|
*
|
||||||
* When allow_poly is false, we are not expecting any of the actual_arg_types
|
* When allow_poly is false, we are not expecting any of the actual_arg_types
|
||||||
* to be polymorphic, and we should not return a polymorphic result type
|
* to be polymorphic, and we should not return a polymorphic result type
|
||||||
@ -1473,7 +1514,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
bool have_unknowns = false;
|
bool have_unknowns = false;
|
||||||
Oid elem_typeid = InvalidOid;
|
Oid elem_typeid = InvalidOid;
|
||||||
Oid array_typeid = InvalidOid;
|
Oid array_typeid = InvalidOid;
|
||||||
|
Oid range_typeid = InvalidOid;
|
||||||
Oid array_typelem;
|
Oid array_typelem;
|
||||||
|
Oid range_typelem;
|
||||||
bool have_anyelement = (rettype == ANYELEMENTOID ||
|
bool have_anyelement = (rettype == ANYELEMENTOID ||
|
||||||
rettype == ANYNONARRAYOID ||
|
rettype == ANYNONARRAYOID ||
|
||||||
rettype == ANYENUMOID);
|
rettype == ANYENUMOID);
|
||||||
@ -1534,6 +1577,26 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
format_type_be(actual_type))));
|
format_type_be(actual_type))));
|
||||||
array_typeid = actual_type;
|
array_typeid = actual_type;
|
||||||
}
|
}
|
||||||
|
else if (decl_type == ANYRANGEOID)
|
||||||
|
{
|
||||||
|
have_generics = true;
|
||||||
|
if (actual_type == UNKNOWNOID)
|
||||||
|
{
|
||||||
|
have_unknowns = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (allow_poly && decl_type == actual_type)
|
||||||
|
continue; /* no new information here */
|
||||||
|
actual_type = getBaseType(actual_type); /* flatten domains */
|
||||||
|
if (OidIsValid(range_typeid) && actual_type != range_typeid)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("arguments declared \"anyrange\" are not all alike"),
|
||||||
|
errdetail("%s versus %s",
|
||||||
|
format_type_be(range_typeid),
|
||||||
|
format_type_be(actual_type))));
|
||||||
|
range_typeid = actual_type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1579,6 +1642,34 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
format_type_be(elem_typeid))));
|
format_type_be(elem_typeid))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Get the element type based on the range type, if we have one */
|
||||||
|
else if (OidIsValid(range_typeid))
|
||||||
|
{
|
||||||
|
range_typelem = get_range_subtype(range_typeid);
|
||||||
|
if (!OidIsValid(range_typelem))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("argument declared \"anyrange\" is not a range but type %s",
|
||||||
|
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 \"anyrange\" is not consistent with argument declared \"anyelement\""),
|
||||||
|
errdetail("%s versus %s",
|
||||||
|
format_type_be(range_typeid),
|
||||||
|
format_type_be(elem_typeid))));
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (!OidIsValid(elem_typeid))
|
else if (!OidIsValid(elem_typeid))
|
||||||
{
|
{
|
||||||
if (allow_poly)
|
if (allow_poly)
|
||||||
@ -1645,6 +1736,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
}
|
}
|
||||||
declared_arg_types[j] = array_typeid;
|
declared_arg_types[j] = array_typeid;
|
||||||
}
|
}
|
||||||
|
else if (decl_type == ANYRANGEOID)
|
||||||
|
{
|
||||||
|
if (!OidIsValid(range_typeid))
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("could not find range type for data type %s",
|
||||||
|
format_type_be(elem_typeid))));
|
||||||
|
}
|
||||||
|
declared_arg_types[j] = range_typeid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1663,6 +1765,19 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
|||||||
return array_typeid;
|
return array_typeid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if we return ANYRANGE use the appropriate argument type */
|
||||||
|
if (rettype == ANYRANGEOID)
|
||||||
|
{
|
||||||
|
if (!OidIsValid(range_typeid))
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("could not find range type for data type %s",
|
||||||
|
format_type_be(elem_typeid))));
|
||||||
|
}
|
||||||
|
return range_typeid;
|
||||||
|
}
|
||||||
|
|
||||||
/* if we return ANYELEMENT use the appropriate argument type */
|
/* if we return ANYELEMENT use the appropriate argument type */
|
||||||
if (rettype == ANYELEMENTOID ||
|
if (rettype == ANYELEMENTOID ||
|
||||||
rettype == ANYNONARRAYOID ||
|
rettype == ANYNONARRAYOID ||
|
||||||
@ -1711,7 +1826,8 @@ resolve_generic_type(Oid declared_type,
|
|||||||
}
|
}
|
||||||
else if (context_declared_type == ANYELEMENTOID ||
|
else if (context_declared_type == ANYELEMENTOID ||
|
||||||
context_declared_type == ANYNONARRAYOID ||
|
context_declared_type == ANYNONARRAYOID ||
|
||||||
context_declared_type == ANYENUMOID)
|
context_declared_type == ANYENUMOID ||
|
||||||
|
context_declared_type == ANYRANGEOID)
|
||||||
{
|
{
|
||||||
/* Use the array type corresponding to actual type */
|
/* Use the array type corresponding to actual type */
|
||||||
Oid array_typeid = get_array_type(context_actual_type);
|
Oid array_typeid = get_array_type(context_actual_type);
|
||||||
@ -1726,7 +1842,8 @@ resolve_generic_type(Oid declared_type,
|
|||||||
}
|
}
|
||||||
else if (declared_type == ANYELEMENTOID ||
|
else if (declared_type == ANYELEMENTOID ||
|
||||||
declared_type == ANYNONARRAYOID ||
|
declared_type == ANYNONARRAYOID ||
|
||||||
declared_type == ANYENUMOID)
|
declared_type == ANYENUMOID ||
|
||||||
|
declared_type == ANYRANGEOID)
|
||||||
{
|
{
|
||||||
if (context_declared_type == ANYARRAYOID)
|
if (context_declared_type == ANYARRAYOID)
|
||||||
{
|
{
|
||||||
@ -1741,6 +1858,18 @@ resolve_generic_type(Oid declared_type,
|
|||||||
format_type_be(context_base_type))));
|
format_type_be(context_base_type))));
|
||||||
return array_typelem;
|
return array_typelem;
|
||||||
}
|
}
|
||||||
|
else if (context_declared_type == ANYRANGEOID)
|
||||||
|
{
|
||||||
|
/* Use the element type corresponding to actual type */
|
||||||
|
Oid range_typelem = get_range_subtype(context_actual_type);
|
||||||
|
|
||||||
|
if (!OidIsValid(range_typelem))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("argument declared \"anyrange\" is not a range but type %s",
|
||||||
|
format_type_be(context_actual_type))));
|
||||||
|
return range_typelem;
|
||||||
|
}
|
||||||
else if (context_declared_type == ANYELEMENTOID ||
|
else if (context_declared_type == ANYELEMENTOID ||
|
||||||
context_declared_type == ANYNONARRAYOID ||
|
context_declared_type == ANYNONARRAYOID ||
|
||||||
context_declared_type == ANYENUMOID)
|
context_declared_type == ANYENUMOID)
|
||||||
@ -1854,6 +1983,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
|
|||||||
if (type_is_enum(srctype))
|
if (type_is_enum(srctype))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
/* Also accept any range type as coercible to ANYRANGE */
|
||||||
|
if (targettype == ANYRANGEOID)
|
||||||
|
if (type_is_range(srctype))
|
||||||
|
return true;
|
||||||
|
|
||||||
/* Also accept any composite type as coercible to RECORD */
|
/* Also accept any composite type as coercible to RECORD */
|
||||||
if (targettype == RECORDOID)
|
if (targettype == RECORDOID)
|
||||||
if (ISCOMPLEX(srctype))
|
if (ISCOMPLEX(srctype))
|
||||||
|
@ -202,6 +202,7 @@ check_xact_readonly(Node *parsetree)
|
|||||||
case T_CreateTrigStmt:
|
case T_CreateTrigStmt:
|
||||||
case T_CompositeTypeStmt:
|
case T_CompositeTypeStmt:
|
||||||
case T_CreateEnumStmt:
|
case T_CreateEnumStmt:
|
||||||
|
case T_CreateRangeStmt:
|
||||||
case T_AlterEnumStmt:
|
case T_AlterEnumStmt:
|
||||||
case T_ViewStmt:
|
case T_ViewStmt:
|
||||||
case T_DropCastStmt:
|
case T_DropCastStmt:
|
||||||
@ -870,6 +871,10 @@ standard_ProcessUtility(Node *parsetree,
|
|||||||
DefineEnum((CreateEnumStmt *) parsetree);
|
DefineEnum((CreateEnumStmt *) parsetree);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_CreateRangeStmt:
|
||||||
|
DefineRange((CreateRangeStmt *) parsetree);
|
||||||
|
break;
|
||||||
|
|
||||||
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
|
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1854,6 +1859,10 @@ CreateCommandTag(Node *parsetree)
|
|||||||
tag = "CREATE TYPE";
|
tag = "CREATE TYPE";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_CreateRangeStmt:
|
||||||
|
tag = "CREATE TYPE";
|
||||||
|
break;
|
||||||
|
|
||||||
case T_AlterEnumStmt:
|
case T_AlterEnumStmt:
|
||||||
tag = "ALTER TYPE";
|
tag = "ALTER TYPE";
|
||||||
break;
|
break;
|
||||||
@ -2401,6 +2410,10 @@ GetCommandLogLevel(Node *parsetree)
|
|||||||
lev = LOGSTMT_DDL;
|
lev = LOGSTMT_DDL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_CreateRangeStmt:
|
||||||
|
lev = LOGSTMT_DDL;
|
||||||
|
break;
|
||||||
|
|
||||||
case T_AlterEnumStmt:
|
case T_AlterEnumStmt:
|
||||||
lev = LOGSTMT_DDL;
|
lev = LOGSTMT_DDL;
|
||||||
break;
|
break;
|
||||||
|
@ -20,8 +20,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
|
|||||||
enum.o float.o format_type.o \
|
enum.o float.o format_type.o \
|
||||||
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
|
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
|
||||||
misc.o nabstime.o name.o numeric.o numutils.o \
|
misc.o nabstime.o name.o numeric.o numutils.o \
|
||||||
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
|
oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.o \
|
||||||
regexp.o regproc.o ruleutils.o selfuncs.o \
|
rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
|
||||||
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
|
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
|
||||||
network.o mac.o inet_cidr_ntop.o inet_net_pton.o \
|
network.o mac.o inet_cidr_ntop.o inet_net_pton.o \
|
||||||
ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
|
ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
|
||||||
|
@ -889,7 +889,6 @@ date_timestamp(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_TIMESTAMP(result);
|
PG_RETURN_TIMESTAMP(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* timestamp_date()
|
/* timestamp_date()
|
||||||
* Convert timestamp to date data type.
|
* Convert timestamp to date data type.
|
||||||
*/
|
*/
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/rangetypes.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -187,6 +188,29 @@ anyenum_out(PG_FUNCTION_ARGS)
|
|||||||
return enum_out(fcinfo);
|
return enum_out(fcinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* anyrange_in - input routine for pseudo-type ANYRANGE.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
anyrange_in(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot accept a value of type anyrange")));
|
||||||
|
|
||||||
|
PG_RETURN_VOID(); /* keep compiler quiet */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* anyrange_out - output routine for pseudo-type ANYRANGE.
|
||||||
|
*
|
||||||
|
* We may as well allow this, since range_out will in fact work.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
anyrange_out(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
return range_out(fcinfo);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* void_in - input routine for pseudo-type VOID.
|
* void_in - input routine for pseudo-type VOID.
|
||||||
|
2153
src/backend/utils/adt/rangetypes.c
Normal file
2153
src/backend/utils/adt/rangetypes.c
Normal file
File diff suppressed because it is too large
Load Diff
587
src/backend/utils/adt/rangetypes_gist.c
Normal file
587
src/backend/utils/adt/rangetypes_gist.c
Normal file
@ -0,0 +1,587 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* rangetypes_gist.c
|
||||||
|
* GiST support for range types.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006-2011, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* src/backend/utils/adt/rangetypes_gist.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/gist.h"
|
||||||
|
#include "access/skey.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/rangetypes.h"
|
||||||
|
|
||||||
|
#define RANGESTRAT_EQ 1
|
||||||
|
#define RANGESTRAT_NE 2
|
||||||
|
#define RANGESTRAT_OVERLAPS 3
|
||||||
|
#define RANGESTRAT_CONTAINS_ELEM 4
|
||||||
|
#define RANGESTRAT_ELEM_CONTAINED_BY 5
|
||||||
|
#define RANGESTRAT_CONTAINS 6
|
||||||
|
#define RANGESTRAT_CONTAINED_BY 7
|
||||||
|
#define RANGESTRAT_BEFORE 8
|
||||||
|
#define RANGESTRAT_AFTER 9
|
||||||
|
#define RANGESTRAT_OVERLEFT 10
|
||||||
|
#define RANGESTRAT_OVERRIGHT 11
|
||||||
|
#define RANGESTRAT_ADJACENT 12
|
||||||
|
|
||||||
|
static RangeType *range_super_union(FunctionCallInfo fcinfo, RangeType *r1,
|
||||||
|
RangeType *r2);
|
||||||
|
static bool range_gist_consistent_int(FunctionCallInfo fcinfo,
|
||||||
|
StrategyNumber strategy, RangeType *key,
|
||||||
|
RangeType *query);
|
||||||
|
static bool range_gist_consistent_leaf(FunctionCallInfo fcinfo,
|
||||||
|
StrategyNumber strategy, RangeType *key,
|
||||||
|
RangeType *query);
|
||||||
|
static int sort_item_cmp(const void *a, const void *b);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Auxiliary structure for picksplit method.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
RangeType *data;
|
||||||
|
FunctionCallInfo fcinfo;
|
||||||
|
} PickSplitSortItem;
|
||||||
|
|
||||||
|
|
||||||
|
Datum
|
||||||
|
range_gist_consistent(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||||
|
Datum dquery = PG_GETARG_DATUM(1);
|
||||||
|
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
|
||||||
|
/* Oid subtype = PG_GETARG_OID(3); */
|
||||||
|
bool *recheck = (bool *) PG_GETARG_POINTER(4);
|
||||||
|
RangeType *key = DatumGetRangeType(entry->key);
|
||||||
|
RangeType *query;
|
||||||
|
|
||||||
|
RangeBound lower;
|
||||||
|
RangeBound upper;
|
||||||
|
bool empty;
|
||||||
|
Oid rngtypid;
|
||||||
|
|
||||||
|
*recheck = false;
|
||||||
|
range_deserialize(fcinfo, key, &lower, &upper, &empty);
|
||||||
|
rngtypid = lower.rngtypid;
|
||||||
|
|
||||||
|
switch (strategy)
|
||||||
|
{
|
||||||
|
RangeBound lower;
|
||||||
|
RangeBound upper;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For contains and contained by operators, the other operand is a
|
||||||
|
* "point" of the subtype. Construct a singleton range containing just
|
||||||
|
* that value.
|
||||||
|
*/
|
||||||
|
case RANGESTRAT_CONTAINS_ELEM:
|
||||||
|
case RANGESTRAT_ELEM_CONTAINED_BY:
|
||||||
|
lower.rngtypid = rngtypid;
|
||||||
|
lower.inclusive = true;
|
||||||
|
lower.val = dquery;
|
||||||
|
lower.lower = true;
|
||||||
|
lower.infinite = false;
|
||||||
|
upper.rngtypid = rngtypid;
|
||||||
|
upper.inclusive = true;
|
||||||
|
upper.val = dquery;
|
||||||
|
upper.lower = false;
|
||||||
|
upper.infinite = false;
|
||||||
|
query = DatumGetRangeType(
|
||||||
|
make_range(fcinfo, &lower, &upper, false));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
query = DatumGetRangeType(dquery);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GIST_LEAF(entry))
|
||||||
|
PG_RETURN_BOOL(range_gist_consistent_leaf(
|
||||||
|
fcinfo, strategy, key, query));
|
||||||
|
else
|
||||||
|
PG_RETURN_BOOL(range_gist_consistent_int(
|
||||||
|
fcinfo, strategy, key, query));
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
range_gist_union(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
|
||||||
|
GISTENTRY *ent = entryvec->vector;
|
||||||
|
RangeType *result_range;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
result_range = DatumGetRangeType(ent[0].key);
|
||||||
|
|
||||||
|
for (i = 1; i < entryvec->n; i++)
|
||||||
|
{
|
||||||
|
result_range = range_super_union(fcinfo, result_range,
|
||||||
|
DatumGetRangeType(ent[i].key));
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_RANGE(result_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
range_gist_compress(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||||
|
PG_RETURN_POINTER(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
range_gist_decompress(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||||
|
PG_RETURN_POINTER(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
range_gist_penalty(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
|
||||||
|
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
|
||||||
|
float *penalty = (float *) PG_GETARG_POINTER(2);
|
||||||
|
RangeType *orig = DatumGetRangeType(origentry->key);
|
||||||
|
RangeType *new = DatumGetRangeType(newentry->key);
|
||||||
|
RangeType *s_union = range_super_union(fcinfo, orig, new);
|
||||||
|
|
||||||
|
FmgrInfo *subtype_diff;
|
||||||
|
|
||||||
|
RangeBound lower1, lower2;
|
||||||
|
RangeBound upper1, upper2;
|
||||||
|
bool empty1, empty2;
|
||||||
|
|
||||||
|
float lower_diff, upper_diff;
|
||||||
|
|
||||||
|
RangeTypeInfo rngtypinfo;
|
||||||
|
|
||||||
|
range_deserialize(fcinfo, orig, &lower1, &upper1, &empty1);
|
||||||
|
range_deserialize(fcinfo, s_union, &lower2, &upper2, &empty2);
|
||||||
|
|
||||||
|
range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);
|
||||||
|
subtype_diff = &rngtypinfo.subdiffFn;
|
||||||
|
|
||||||
|
Assert(empty1 || !empty2);
|
||||||
|
|
||||||
|
if (empty1 && empty2)
|
||||||
|
return 0;
|
||||||
|
else if (empty1 && !empty2)
|
||||||
|
{
|
||||||
|
if (lower2.infinite || upper2.infinite)
|
||||||
|
/* from empty to infinite */
|
||||||
|
return get_float8_infinity();
|
||||||
|
else if (subtype_diff->fn_addr != NULL)
|
||||||
|
/* from empty to upper2-lower2 */
|
||||||
|
return DatumGetFloat8(FunctionCall2(subtype_diff,
|
||||||
|
upper2.val, lower2.val));
|
||||||
|
else
|
||||||
|
/* wild guess */
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(lower2.infinite || !lower1.infinite);
|
||||||
|
|
||||||
|
if (lower2.infinite && !lower1.infinite)
|
||||||
|
lower_diff = get_float8_infinity();
|
||||||
|
else if (lower2.infinite && lower1.infinite)
|
||||||
|
lower_diff = 0;
|
||||||
|
else if (subtype_diff->fn_addr != NULL)
|
||||||
|
{
|
||||||
|
lower_diff = DatumGetFloat8(FunctionCall2(subtype_diff,
|
||||||
|
lower1.val, lower2.val));
|
||||||
|
if (lower_diff < 0)
|
||||||
|
lower_diff = 0; /* subtype_diff is broken */
|
||||||
|
}
|
||||||
|
else /* only know whether there is a difference or not */
|
||||||
|
lower_diff = (float) range_cmp_bounds(fcinfo, &lower1, &lower2);
|
||||||
|
|
||||||
|
Assert(upper2.infinite || !upper1.infinite);
|
||||||
|
|
||||||
|
if (upper2.infinite && !upper1.infinite)
|
||||||
|
upper_diff = get_float8_infinity();
|
||||||
|
else if (upper2.infinite && upper1.infinite)
|
||||||
|
upper_diff = 0;
|
||||||
|
else if (subtype_diff->fn_addr != NULL)
|
||||||
|
{
|
||||||
|
upper_diff = DatumGetFloat8(FunctionCall2(subtype_diff,
|
||||||
|
upper2.val, upper1.val));
|
||||||
|
if (upper_diff < 0)
|
||||||
|
upper_diff = 0; /* subtype_diff is broken */
|
||||||
|
}
|
||||||
|
else /* only know whether there is a difference or not */
|
||||||
|
upper_diff = (float) range_cmp_bounds(fcinfo, &upper2, &upper1);
|
||||||
|
|
||||||
|
Assert(lower_diff >= 0 && upper_diff >= 0);
|
||||||
|
|
||||||
|
*penalty = (float) (lower_diff + upper_diff);
|
||||||
|
PG_RETURN_POINTER(penalty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The GiST PickSplit method for ranges
|
||||||
|
*
|
||||||
|
* Algorithm based on sorting. Incoming array of periods is sorted using
|
||||||
|
* sort_item_cmp function. After that first half of periods goes to the left
|
||||||
|
* datum, and the second half of periods goes to the right datum.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
range_gist_picksplit(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
|
||||||
|
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
|
||||||
|
OffsetNumber i;
|
||||||
|
RangeType *pred_left;
|
||||||
|
RangeType *pred_right;
|
||||||
|
PickSplitSortItem *sortItems;
|
||||||
|
int nbytes;
|
||||||
|
OffsetNumber split_idx;
|
||||||
|
OffsetNumber *left;
|
||||||
|
OffsetNumber *right;
|
||||||
|
OffsetNumber maxoff;
|
||||||
|
|
||||||
|
maxoff = entryvec->n - 1;
|
||||||
|
nbytes = (maxoff + 1) * sizeof(OffsetNumber);
|
||||||
|
sortItems = (PickSplitSortItem *) palloc(
|
||||||
|
maxoff * sizeof(PickSplitSortItem));
|
||||||
|
v->spl_left = (OffsetNumber *) palloc(nbytes);
|
||||||
|
v->spl_right = (OffsetNumber *) palloc(nbytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Preparing auxiliary array and sorting.
|
||||||
|
*/
|
||||||
|
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
|
||||||
|
{
|
||||||
|
sortItems[i - 1].index = i;
|
||||||
|
sortItems[i - 1].data = DatumGetRangeType(entryvec->vector[i].key);
|
||||||
|
sortItems[i - 1].fcinfo = fcinfo;
|
||||||
|
}
|
||||||
|
qsort(sortItems, maxoff, sizeof(PickSplitSortItem), sort_item_cmp);
|
||||||
|
split_idx = maxoff / 2;
|
||||||
|
|
||||||
|
left = v->spl_left;
|
||||||
|
v->spl_nleft = 0;
|
||||||
|
right = v->spl_right;
|
||||||
|
v->spl_nright = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First half of segs goes to the left datum.
|
||||||
|
*/
|
||||||
|
pred_left = DatumGetRangeType(sortItems[0].data);
|
||||||
|
*left++ = sortItems[0].index;
|
||||||
|
v->spl_nleft++;
|
||||||
|
for (i = 1; i < split_idx; i++)
|
||||||
|
{
|
||||||
|
pred_left = range_super_union(fcinfo, pred_left,
|
||||||
|
DatumGetRangeType(sortItems[i].data));
|
||||||
|
*left++ = sortItems[i].index;
|
||||||
|
v->spl_nleft++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Second half of segs goes to the right datum.
|
||||||
|
*/
|
||||||
|
pred_right = DatumGetRangeType(sortItems[split_idx].data);
|
||||||
|
*right++ = sortItems[split_idx].index;
|
||||||
|
v->spl_nright++;
|
||||||
|
for (i = split_idx + 1; i < maxoff; i++)
|
||||||
|
{
|
||||||
|
pred_right = range_super_union(fcinfo, pred_right,
|
||||||
|
DatumGetRangeType(sortItems[i].data));
|
||||||
|
*right++ = sortItems[i].index;
|
||||||
|
v->spl_nright++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */
|
||||||
|
|
||||||
|
v->spl_ldatum = RangeTypeGetDatum(pred_left);
|
||||||
|
v->spl_rdatum = RangeTypeGetDatum(pred_right);
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
range_gist_same(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Datum r1 = PG_GETARG_DATUM(0);
|
||||||
|
Datum r2 = PG_GETARG_DATUM(1);
|
||||||
|
bool *result = (bool *) PG_GETARG_POINTER(2);
|
||||||
|
|
||||||
|
*result = DatumGetBool(OidFunctionCall2(F_RANGE_EQ, r1, r2));
|
||||||
|
PG_RETURN_POINTER(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*----------------------------------------------------------
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
*----------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* return the smallest range that contains r1 and r2 */
|
||||||
|
static RangeType *
|
||||||
|
range_super_union(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
|
||||||
|
{
|
||||||
|
RangeBound lower1, lower2;
|
||||||
|
RangeBound upper1, upper2;
|
||||||
|
bool empty1, empty2;
|
||||||
|
RangeBound *result_lower;
|
||||||
|
RangeBound *result_upper;
|
||||||
|
|
||||||
|
range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
|
||||||
|
range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
|
||||||
|
|
||||||
|
if (empty1)
|
||||||
|
return r2;
|
||||||
|
if (empty2)
|
||||||
|
return r1;
|
||||||
|
|
||||||
|
if (range_cmp_bounds(fcinfo, &lower1, &lower2) <= 0)
|
||||||
|
result_lower = &lower1;
|
||||||
|
else
|
||||||
|
result_lower = &lower2;
|
||||||
|
|
||||||
|
if (range_cmp_bounds(fcinfo, &upper1, &upper2) >= 0)
|
||||||
|
result_upper = &upper1;
|
||||||
|
else
|
||||||
|
result_upper = &upper2;
|
||||||
|
|
||||||
|
/* optimization to avoid constructing a new range */
|
||||||
|
if (result_lower == &lower1 && result_upper == &upper1)
|
||||||
|
return r1;
|
||||||
|
if (result_lower == &lower2 && result_upper == &upper2)
|
||||||
|
return r2;
|
||||||
|
|
||||||
|
return DatumGetRangeType(
|
||||||
|
make_range(fcinfo, result_lower, result_upper, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
range_gist_consistent_int(FunctionCallInfo fcinfo, StrategyNumber strategy,
|
||||||
|
RangeType *key, RangeType *query)
|
||||||
|
{
|
||||||
|
Oid proc = InvalidOid;
|
||||||
|
|
||||||
|
RangeBound lower1, lower2;
|
||||||
|
RangeBound upper1, upper2;
|
||||||
|
bool empty1, empty2;
|
||||||
|
|
||||||
|
bool retval;
|
||||||
|
bool negate = false;
|
||||||
|
|
||||||
|
range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
|
||||||
|
range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
|
||||||
|
|
||||||
|
switch (strategy)
|
||||||
|
{
|
||||||
|
case RANGESTRAT_EQ:
|
||||||
|
proc = F_RANGE_CONTAINS;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_NE:
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_OVERLAPS:
|
||||||
|
proc = F_RANGE_OVERLAPS;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_CONTAINS_ELEM:
|
||||||
|
case RANGESTRAT_CONTAINS:
|
||||||
|
proc = F_RANGE_CONTAINS;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_ELEM_CONTAINED_BY:
|
||||||
|
case RANGESTRAT_CONTAINED_BY:
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_BEFORE:
|
||||||
|
if (empty1)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_OVERRIGHT;
|
||||||
|
negate = true;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_AFTER:
|
||||||
|
if (empty1)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_OVERLEFT;
|
||||||
|
negate = true;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_OVERLEFT:
|
||||||
|
if (empty1)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_AFTER;
|
||||||
|
negate = true;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_OVERRIGHT:
|
||||||
|
if (empty1)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_BEFORE;
|
||||||
|
negate = true;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_ADJACENT:
|
||||||
|
if (empty1 || empty2)
|
||||||
|
return false;
|
||||||
|
if (DatumGetBool(
|
||||||
|
OidFunctionCall2(F_RANGE_ADJACENT,
|
||||||
|
RangeTypeGetDatum(key),
|
||||||
|
RangeTypeGetDatum(query))))
|
||||||
|
return true;
|
||||||
|
proc = F_RANGE_OVERLAPS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
|
||||||
|
RangeTypeGetDatum(query)));
|
||||||
|
|
||||||
|
if (negate)
|
||||||
|
retval = !retval;
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
range_gist_consistent_leaf(FunctionCallInfo fcinfo, StrategyNumber strategy,
|
||||||
|
RangeType *key, RangeType *query)
|
||||||
|
{
|
||||||
|
Oid proc = InvalidOid;
|
||||||
|
|
||||||
|
RangeBound lower1, lower2;
|
||||||
|
RangeBound upper1, upper2;
|
||||||
|
bool empty1, empty2;
|
||||||
|
|
||||||
|
range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
|
||||||
|
range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
|
||||||
|
|
||||||
|
switch (strategy)
|
||||||
|
{
|
||||||
|
case RANGESTRAT_EQ:
|
||||||
|
proc = F_RANGE_EQ;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_NE:
|
||||||
|
proc = F_RANGE_NE;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_OVERLAPS:
|
||||||
|
proc = F_RANGE_OVERLAPS;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_CONTAINS_ELEM:
|
||||||
|
case RANGESTRAT_CONTAINS:
|
||||||
|
proc = F_RANGE_CONTAINS;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_ELEM_CONTAINED_BY:
|
||||||
|
case RANGESTRAT_CONTAINED_BY:
|
||||||
|
proc = F_RANGE_CONTAINED_BY;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_BEFORE:
|
||||||
|
if (empty1 || empty2)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_BEFORE;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_AFTER:
|
||||||
|
if (empty1 || empty2)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_AFTER;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_OVERLEFT:
|
||||||
|
if (empty1 || empty2)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_OVERLEFT;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_OVERRIGHT:
|
||||||
|
if (empty1 || empty2)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_OVERRIGHT;
|
||||||
|
break;
|
||||||
|
case RANGESTRAT_ADJACENT:
|
||||||
|
if (empty1 || empty2)
|
||||||
|
return false;
|
||||||
|
proc = F_RANGE_ADJACENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
|
||||||
|
RangeTypeGetDatum(query)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare function for PickSplitSortItem. This is actually the
|
||||||
|
* interesting part of the picksplit algorithm.
|
||||||
|
*
|
||||||
|
* We want to separate out empty ranges, bounded ranges, and unbounded
|
||||||
|
* ranges. We assume that "contains" and "overlaps" are the most
|
||||||
|
* important queries, so empty ranges will rarely match and unbounded
|
||||||
|
* ranges frequently will. Bounded ranges should be in the middle.
|
||||||
|
*
|
||||||
|
* Empty ranges we push all the way to the left, then bounded ranges
|
||||||
|
* (sorted on lower bound, then upper), then ranges with no lower
|
||||||
|
* bound, then ranges with no upper bound; and finally, ranges with no
|
||||||
|
* upper or lower bound all the way to the right.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
sort_item_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
PickSplitSortItem *i1 = (PickSplitSortItem *)a;
|
||||||
|
PickSplitSortItem *i2 = (PickSplitSortItem *)b;
|
||||||
|
RangeType *r1 = i1->data;
|
||||||
|
RangeType *r2 = i2->data;
|
||||||
|
|
||||||
|
RangeBound lower1, lower2;
|
||||||
|
RangeBound upper1, upper2;
|
||||||
|
bool empty1, empty2;
|
||||||
|
|
||||||
|
FunctionCallInfo fcinfo = i1->fcinfo;
|
||||||
|
|
||||||
|
int cmp;
|
||||||
|
|
||||||
|
range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
|
||||||
|
range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
|
||||||
|
|
||||||
|
if (empty1 || empty2)
|
||||||
|
{
|
||||||
|
if (empty1 && empty2)
|
||||||
|
return 0;
|
||||||
|
else if (empty1)
|
||||||
|
return -1;
|
||||||
|
else if (empty2)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
Assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If both lower or both upper bounds are infinite, we sort by
|
||||||
|
* ascending range size. That means that if both upper bounds are
|
||||||
|
* infinite, we sort by the lower bound _descending_. That creates
|
||||||
|
* a slightly odd total order, but keeps the pages with very
|
||||||
|
* unselective predicates grouped more closely together on the
|
||||||
|
* right.
|
||||||
|
*/
|
||||||
|
if (lower1.infinite || upper1.infinite ||
|
||||||
|
lower2.infinite || upper2.infinite)
|
||||||
|
{
|
||||||
|
if (lower1.infinite && lower2.infinite)
|
||||||
|
return range_cmp_bounds(fcinfo, &upper1, &upper2);
|
||||||
|
else if (lower1.infinite)
|
||||||
|
return -1;
|
||||||
|
else if (lower2.infinite)
|
||||||
|
return 1;
|
||||||
|
else if (upper1.infinite && upper2.infinite)
|
||||||
|
return -1 * range_cmp_bounds(fcinfo, &lower1, &lower2);
|
||||||
|
else if (upper1.infinite)
|
||||||
|
return 1;
|
||||||
|
else if (upper2.infinite)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
Assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
|
||||||
|
return cmp;
|
||||||
|
|
||||||
|
return range_cmp_bounds(fcinfo, &upper1, &upper2);
|
||||||
|
}
|
30
src/backend/utils/cache/lsyscache.c
vendored
30
src/backend/utils/cache/lsyscache.c
vendored
@ -26,6 +26,7 @@
|
|||||||
#include "catalog/pg_opclass.h"
|
#include "catalog/pg_opclass.h"
|
||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "catalog/pg_range.h"
|
||||||
#include "catalog/pg_statistic.h"
|
#include "catalog/pg_statistic.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -2250,6 +2251,16 @@ type_is_enum(Oid typid)
|
|||||||
return (get_typtype(typid) == TYPTYPE_ENUM);
|
return (get_typtype(typid) == TYPTYPE_ENUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* type_is_range
|
||||||
|
* Returns true if the given type is an range type.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
type_is_range(Oid typid)
|
||||||
|
{
|
||||||
|
return (get_typtype(typid) == TYPTYPE_RANGE);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_type_category_preferred
|
* get_type_category_preferred
|
||||||
*
|
*
|
||||||
@ -2855,3 +2866,22 @@ get_namespace_name(Oid nspid)
|
|||||||
else
|
else
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Oid
|
||||||
|
get_range_subtype(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->rngsubtype;
|
||||||
|
ReleaseSysCache(tp);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return InvalidOid;
|
||||||
|
}
|
||||||
|
12
src/backend/utils/cache/syscache.c
vendored
12
src/backend/utils/cache/syscache.c
vendored
@ -43,6 +43,7 @@
|
|||||||
#include "catalog/pg_operator.h"
|
#include "catalog/pg_operator.h"
|
||||||
#include "catalog/pg_opfamily.h"
|
#include "catalog/pg_opfamily.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "catalog/pg_range.h"
|
||||||
#include "catalog/pg_rewrite.h"
|
#include "catalog/pg_rewrite.h"
|
||||||
#include "catalog/pg_statistic.h"
|
#include "catalog/pg_statistic.h"
|
||||||
#include "catalog/pg_tablespace.h"
|
#include "catalog/pg_tablespace.h"
|
||||||
@ -554,6 +555,17 @@ static const struct cachedesc cacheinfo[] = {
|
|||||||
},
|
},
|
||||||
2048
|
2048
|
||||||
},
|
},
|
||||||
|
{RangeRelationId, /* RANGETYPE */
|
||||||
|
RangeTypidIndexId,
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
Anum_pg_range_rngtypid,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
},
|
||||||
|
1024
|
||||||
|
},
|
||||||
{RelationRelationId, /* RELNAMENSP */
|
{RelationRelationId, /* RELNAMENSP */
|
||||||
ClassNameNspIndexId,
|
ClassNameNspIndexId,
|
||||||
2,
|
2,
|
||||||
|
@ -407,11 +407,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
|
|||||||
int nargs = declared_args->dim1;
|
int nargs = declared_args->dim1;
|
||||||
bool have_anyelement_result = false;
|
bool have_anyelement_result = false;
|
||||||
bool have_anyarray_result = false;
|
bool have_anyarray_result = false;
|
||||||
|
bool have_anyrange_result = false;
|
||||||
bool have_anynonarray = false;
|
bool have_anynonarray = false;
|
||||||
bool have_anyenum = false;
|
bool have_anyenum = false;
|
||||||
Oid anyelement_type = InvalidOid;
|
Oid anyelement_type = InvalidOid;
|
||||||
Oid anyarray_type = InvalidOid;
|
Oid anyarray_type = InvalidOid;
|
||||||
Oid anycollation;
|
Oid anyrange_type = InvalidOid;
|
||||||
|
Oid anycollation = InvalidOid;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* See if there are any polymorphic outputs; quick out if not */
|
/* See if there are any polymorphic outputs; quick out if not */
|
||||||
@ -433,11 +435,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
|
|||||||
have_anyelement_result = true;
|
have_anyelement_result = true;
|
||||||
have_anyenum = true;
|
have_anyenum = true;
|
||||||
break;
|
break;
|
||||||
|
case ANYRANGEOID:
|
||||||
|
have_anyrange_result = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!have_anyelement_result && !have_anyarray_result)
|
if (!have_anyelement_result && !have_anyarray_result &&
|
||||||
|
!have_anyrange_result)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -461,20 +467,47 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
|
|||||||
if (!OidIsValid(anyarray_type))
|
if (!OidIsValid(anyarray_type))
|
||||||
anyarray_type = get_call_expr_argtype(call_expr, i);
|
anyarray_type = get_call_expr_argtype(call_expr, i);
|
||||||
break;
|
break;
|
||||||
|
case ANYRANGEOID:
|
||||||
|
if (!OidIsValid(anyrange_type))
|
||||||
|
anyrange_type = get_call_expr_argtype(call_expr, i);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If nothing found, parser messed up */
|
/* If nothing found, parser messed up */
|
||||||
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
|
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
|
||||||
|
!OidIsValid(anyrange_type))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can't deduce a range type from the subtype, because there may be
|
||||||
|
* multiple range types for a single subtype.
|
||||||
|
*/
|
||||||
|
if (have_anyrange_result && !OidIsValid(anyrange_type))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* If needed, deduce one polymorphic type from the other */
|
/* If needed, deduce one polymorphic type from the other */
|
||||||
if (have_anyelement_result && !OidIsValid(anyelement_type))
|
if (have_anyelement_result && !OidIsValid(anyelement_type))
|
||||||
anyelement_type = resolve_generic_type(ANYELEMENTOID,
|
{
|
||||||
anyarray_type,
|
if (OidIsValid(anyarray_type))
|
||||||
ANYARRAYOID);
|
anyelement_type = resolve_generic_type(ANYELEMENTOID,
|
||||||
|
anyarray_type,
|
||||||
|
ANYARRAYOID);
|
||||||
|
if (OidIsValid(anyrange_type))
|
||||||
|
{
|
||||||
|
Oid subtype = resolve_generic_type(ANYELEMENTOID,
|
||||||
|
anyrange_type,
|
||||||
|
ANYRANGEOID);
|
||||||
|
if (OidIsValid(anyelement_type) &&
|
||||||
|
anyelement_type != subtype)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
anyelement_type = subtype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (have_anyarray_result && !OidIsValid(anyarray_type))
|
if (have_anyarray_result && !OidIsValid(anyarray_type))
|
||||||
anyarray_type = resolve_generic_type(ANYARRAYOID,
|
anyarray_type = resolve_generic_type(ANYARRAYOID,
|
||||||
anyelement_type,
|
anyelement_type,
|
||||||
@ -492,7 +525,12 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
|
|||||||
* Identify the collation to use for polymorphic OUT parameters. (It'll
|
* Identify the collation to use for polymorphic OUT parameters. (It'll
|
||||||
* necessarily be the same for both anyelement and anyarray.)
|
* necessarily be the same for both anyelement and anyarray.)
|
||||||
*/
|
*/
|
||||||
anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type);
|
|
||||||
|
if (OidIsValid(anyelement_type))
|
||||||
|
anycollation = get_typcollation(anyelement_type);
|
||||||
|
else if (OidIsValid(anyarray_type))
|
||||||
|
anycollation = get_typcollation(anyarray_type);
|
||||||
|
|
||||||
if (OidIsValid(anycollation))
|
if (OidIsValid(anycollation))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -529,6 +567,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
|
|||||||
0);
|
0);
|
||||||
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
|
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
|
||||||
break;
|
break;
|
||||||
|
case ANYRANGEOID:
|
||||||
|
TupleDescInitEntry(tupdesc, i + 1,
|
||||||
|
NameStr(tupdesc->attrs[i]->attname),
|
||||||
|
anyrange_type,
|
||||||
|
-1,
|
||||||
|
0);
|
||||||
|
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -552,8 +598,10 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
|||||||
{
|
{
|
||||||
bool have_anyelement_result = false;
|
bool have_anyelement_result = false;
|
||||||
bool have_anyarray_result = false;
|
bool have_anyarray_result = false;
|
||||||
|
bool have_anyrange_result = false;
|
||||||
Oid anyelement_type = InvalidOid;
|
Oid anyelement_type = InvalidOid;
|
||||||
Oid anyarray_type = InvalidOid;
|
Oid anyarray_type = InvalidOid;
|
||||||
|
Oid anyrange_type = InvalidOid;
|
||||||
int inargno;
|
int inargno;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -597,6 +645,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
|||||||
argtypes[i] = anyarray_type;
|
argtypes[i] = anyarray_type;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ANYRANGEOID:
|
||||||
|
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
|
||||||
|
have_anyrange_result = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!OidIsValid(anyrange_type))
|
||||||
|
{
|
||||||
|
anyrange_type = get_call_expr_argtype(call_expr,
|
||||||
|
inargno);
|
||||||
|
if (!OidIsValid(anyrange_type))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
argtypes[i] = anyrange_type;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -605,18 +668,42 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Done? */
|
/* Done? */
|
||||||
if (!have_anyelement_result && !have_anyarray_result)
|
if (!have_anyelement_result && !have_anyarray_result &&
|
||||||
|
!have_anyrange_result)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can't deduce a range type from the subtype, because there may be
|
||||||
|
* multiple range types for a single subtype.
|
||||||
|
*/
|
||||||
|
if (have_anyrange_result && !OidIsValid(anyrange_type))
|
||||||
|
return false;
|
||||||
|
|
||||||
/* If no input polymorphics, parser messed up */
|
/* If no input polymorphics, parser messed up */
|
||||||
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
|
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
|
||||||
|
!OidIsValid(anyrange_type))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* If needed, deduce one polymorphic type from the other */
|
/* If needed, deduce one polymorphic type from the other */
|
||||||
if (have_anyelement_result && !OidIsValid(anyelement_type))
|
if (have_anyelement_result && !OidIsValid(anyelement_type))
|
||||||
anyelement_type = resolve_generic_type(ANYELEMENTOID,
|
{
|
||||||
anyarray_type,
|
if (OidIsValid(anyarray_type))
|
||||||
ANYARRAYOID);
|
anyelement_type = resolve_generic_type(ANYELEMENTOID,
|
||||||
|
anyarray_type,
|
||||||
|
ANYARRAYOID);
|
||||||
|
if (OidIsValid(anyrange_type))
|
||||||
|
{
|
||||||
|
Oid subtype = resolve_generic_type(ANYELEMENTOID,
|
||||||
|
anyrange_type,
|
||||||
|
ANYRANGEOID);
|
||||||
|
if (OidIsValid(anyelement_type) &&
|
||||||
|
anyelement_type != subtype)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
anyelement_type = subtype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (have_anyarray_result && !OidIsValid(anyarray_type))
|
if (have_anyarray_result && !OidIsValid(anyarray_type))
|
||||||
anyarray_type = resolve_generic_type(ANYARRAYOID,
|
anyarray_type = resolve_generic_type(ANYARRAYOID,
|
||||||
anyelement_type,
|
anyelement_type,
|
||||||
@ -637,6 +724,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
|||||||
case ANYARRAYOID:
|
case ANYARRAYOID:
|
||||||
argtypes[i] = anyarray_type;
|
argtypes[i] = anyarray_type;
|
||||||
break;
|
break;
|
||||||
|
case ANYRANGEOID:
|
||||||
|
argtypes[i] = anyrange_type;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -663,6 +753,7 @@ get_type_func_class(Oid typid)
|
|||||||
case TYPTYPE_BASE:
|
case TYPTYPE_BASE:
|
||||||
case TYPTYPE_DOMAIN:
|
case TYPTYPE_DOMAIN:
|
||||||
case TYPTYPE_ENUM:
|
case TYPTYPE_ENUM:
|
||||||
|
case TYPTYPE_RANGE:
|
||||||
return TYPEFUNC_SCALAR;
|
return TYPEFUNC_SCALAR;
|
||||||
case TYPTYPE_PSEUDO:
|
case TYPTYPE_PSEUDO:
|
||||||
if (typid == RECORDOID)
|
if (typid == RECORDOID)
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
#include "catalog/pg_largeobject.h"
|
#include "catalog/pg_largeobject.h"
|
||||||
#include "catalog/pg_largeobject_metadata.h"
|
#include "catalog/pg_largeobject_metadata.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
|
#include "catalog/pg_range.h"
|
||||||
#include "catalog/pg_trigger.h"
|
#include "catalog/pg_trigger.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "libpq/libpq-fs.h"
|
#include "libpq/libpq-fs.h"
|
||||||
@ -167,6 +168,7 @@ static void dumpExtension(Archive *fout, ExtensionInfo *extinfo);
|
|||||||
static void dumpType(Archive *fout, TypeInfo *tyinfo);
|
static void dumpType(Archive *fout, TypeInfo *tyinfo);
|
||||||
static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
|
static void dumpBaseType(Archive *fout, TypeInfo *tyinfo);
|
||||||
static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
|
static void dumpEnumType(Archive *fout, TypeInfo *tyinfo);
|
||||||
|
static void dumpRangeType(Archive *fout, TypeInfo *tyinfo);
|
||||||
static void dumpDomain(Archive *fout, TypeInfo *tyinfo);
|
static void dumpDomain(Archive *fout, TypeInfo *tyinfo);
|
||||||
static void dumpCompositeType(Archive *fout, TypeInfo *tyinfo);
|
static void dumpCompositeType(Archive *fout, TypeInfo *tyinfo);
|
||||||
static void dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo);
|
static void dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo);
|
||||||
@ -2989,7 +2991,8 @@ getTypes(int *numTypes)
|
|||||||
* should copy the base type's catId, but then it might capture the
|
* should copy the base type's catId, but then it might capture the
|
||||||
* pg_depend entries for the type, which we don't want.
|
* pg_depend entries for the type, which we don't want.
|
||||||
*/
|
*/
|
||||||
if (tyinfo[i].dobj.dump && tyinfo[i].typtype == TYPTYPE_BASE)
|
if (tyinfo[i].dobj.dump && (tyinfo[i].typtype == TYPTYPE_BASE ||
|
||||||
|
tyinfo[i].typtype == TYPTYPE_RANGE))
|
||||||
{
|
{
|
||||||
stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo));
|
stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo));
|
||||||
stinfo->dobj.objType = DO_SHELL_TYPE;
|
stinfo->dobj.objType = DO_SHELL_TYPE;
|
||||||
@ -3700,7 +3703,32 @@ getFuncs(int *numFuncs)
|
|||||||
* so be sure to fetch any such functions.
|
* so be sure to fetch any such functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (g_fout->remoteVersion >= 70300)
|
if (g_fout->remoteVersion >= 90200)
|
||||||
|
{
|
||||||
|
appendPQExpBuffer(query,
|
||||||
|
"SELECT tableoid, oid, proname, prolang, "
|
||||||
|
"pronargs, proargtypes, prorettype, proacl, "
|
||||||
|
"pronamespace, "
|
||||||
|
"(%s proowner) AS rolname "
|
||||||
|
"FROM pg_proc p "
|
||||||
|
"WHERE NOT proisagg AND "
|
||||||
|
" NOT EXISTS (SELECT 1 FROM pg_depend "
|
||||||
|
" WHERE classid = 'pg_proc'::regclass AND "
|
||||||
|
" objid = p.oid AND deptype = 'i') AND "
|
||||||
|
"(pronamespace != "
|
||||||
|
"(SELECT oid FROM pg_namespace "
|
||||||
|
"WHERE nspname = 'pg_catalog')",
|
||||||
|
username_subquery);
|
||||||
|
if (binary_upgrade && g_fout->remoteVersion >= 90100)
|
||||||
|
appendPQExpBuffer(query,
|
||||||
|
" OR EXISTS(SELECT 1 FROM pg_depend WHERE "
|
||||||
|
"classid = 'pg_proc'::regclass AND "
|
||||||
|
"objid = p.oid AND "
|
||||||
|
"refclassid = 'pg_extension'::regclass AND "
|
||||||
|
"deptype = 'e')");
|
||||||
|
appendPQExpBuffer(query, ")");
|
||||||
|
}
|
||||||
|
else if (g_fout->remoteVersion >= 70300)
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(query,
|
appendPQExpBuffer(query,
|
||||||
"SELECT tableoid, oid, proname, prolang, "
|
"SELECT tableoid, oid, proname, prolang, "
|
||||||
@ -7309,6 +7337,8 @@ dumpType(Archive *fout, TypeInfo *tyinfo)
|
|||||||
dumpCompositeType(fout, tyinfo);
|
dumpCompositeType(fout, tyinfo);
|
||||||
else if (tyinfo->typtype == TYPTYPE_ENUM)
|
else if (tyinfo->typtype == TYPTYPE_ENUM)
|
||||||
dumpEnumType(fout, tyinfo);
|
dumpEnumType(fout, tyinfo);
|
||||||
|
else if (tyinfo->typtype == TYPTYPE_RANGE)
|
||||||
|
dumpRangeType(fout, tyinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -7432,6 +7462,156 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
|
|||||||
destroyPQExpBuffer(query);
|
destroyPQExpBuffer(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dumpRangeType
|
||||||
|
* writes out to fout the queries to recreate a user-defined range type
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
dumpRangeType(Archive *fout, TypeInfo *tyinfo)
|
||||||
|
{
|
||||||
|
PQExpBuffer q = createPQExpBuffer();
|
||||||
|
PQExpBuffer delq = createPQExpBuffer();
|
||||||
|
PQExpBuffer labelq = createPQExpBuffer();
|
||||||
|
PQExpBuffer query = createPQExpBuffer();
|
||||||
|
PGresult *res;
|
||||||
|
|
||||||
|
Oid collationOid;
|
||||||
|
Oid opclassOid;
|
||||||
|
Oid analyzeOid;
|
||||||
|
Oid canonicalOid;
|
||||||
|
Oid subdiffOid;
|
||||||
|
|
||||||
|
/* Set proper schema search path */
|
||||||
|
selectSourceSchema("pg_catalog");
|
||||||
|
|
||||||
|
appendPQExpBuffer(query,
|
||||||
|
"SELECT rngtypid, "
|
||||||
|
"format_type(rngsubtype, NULL) as rngsubtype, "
|
||||||
|
"rngsubtype::oid as rngsubtypeoid, "
|
||||||
|
"opc.opcname AS opcname, "
|
||||||
|
"CASE WHEN rngcollation = st.typcollation THEN 0 "
|
||||||
|
" ELSE rngcollation END AS collation, "
|
||||||
|
"CASE WHEN opcdefault THEN 0 ELSE rngsubopc END "
|
||||||
|
" AS rngsubopc, "
|
||||||
|
"(SELECT nspname FROM pg_namespace nsp "
|
||||||
|
" WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
|
||||||
|
"t.typanalyze, t.typanalyze::oid as typanalyzeoid, "
|
||||||
|
"rngcanonical, rngcanonical::oid as rngcanonicaloid, "
|
||||||
|
"rngsubdiff, rngsubdiff::oid as rngsubdiffoid "
|
||||||
|
"FROM pg_catalog.pg_type t, pg_type st, "
|
||||||
|
" pg_catalog.pg_opclass opc, pg_catalog.pg_range r "
|
||||||
|
"WHERE t.oid = rngtypid AND st.oid = rngsubtype AND "
|
||||||
|
" opc.oid = rngsubopc AND rngtypid = '%u'",
|
||||||
|
tyinfo->dobj.catId.oid);
|
||||||
|
|
||||||
|
res = PQexec(g_conn, query->data);
|
||||||
|
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DROP must be fully qualified in case same name appears in pg_catalog.
|
||||||
|
* CASCADE shouldn't be required here as for normal types since the I/O
|
||||||
|
* functions are generic and do not get dropped.
|
||||||
|
*/
|
||||||
|
appendPQExpBuffer(delq, "DROP TYPE %s.",
|
||||||
|
fmtId(tyinfo->dobj.namespace->dobj.name));
|
||||||
|
appendPQExpBuffer(delq, "%s;\n",
|
||||||
|
fmtId(tyinfo->dobj.name));
|
||||||
|
|
||||||
|
/* We might already have a shell type, but setting pg_type_oid is harmless */
|
||||||
|
if (binary_upgrade)
|
||||||
|
binary_upgrade_set_type_oids_by_type_oid(q, tyinfo->dobj.catId.oid);
|
||||||
|
|
||||||
|
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
|
||||||
|
fmtId(tyinfo->dobj.name));
|
||||||
|
|
||||||
|
/* SUBTYPE */
|
||||||
|
appendPQExpBuffer(q, "\n SUBTYPE = %s",
|
||||||
|
PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
|
||||||
|
|
||||||
|
/* COLLATION */
|
||||||
|
collationOid = atooid(PQgetvalue(res, 0,
|
||||||
|
PQfnumber(res, "collation")));
|
||||||
|
if (OidIsValid(collationOid))
|
||||||
|
{
|
||||||
|
CollInfo *coll;
|
||||||
|
|
||||||
|
coll = findCollationByOid(collationOid);
|
||||||
|
if (coll)
|
||||||
|
{
|
||||||
|
/* always schema-qualify, don't try to be smart */
|
||||||
|
appendPQExpBuffer(q, ",\n COLLATION = %s.",
|
||||||
|
fmtId(coll->dobj.namespace->dobj.name));
|
||||||
|
appendPQExpBuffer(q, "%s",
|
||||||
|
fmtId(coll->dobj.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SUBTYPE_OPCLASS */
|
||||||
|
opclassOid = atooid(PQgetvalue(res, 0,
|
||||||
|
PQfnumber(res, "rngsubopc")));
|
||||||
|
if (OidIsValid(opclassOid))
|
||||||
|
{
|
||||||
|
char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
|
||||||
|
char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
|
||||||
|
|
||||||
|
/* always schema-qualify, don't try to be smart */
|
||||||
|
appendPQExpBuffer(q, ",\n SUBTYPE_OPCLASS = %s.",
|
||||||
|
fmtId(nspname));
|
||||||
|
appendPQExpBuffer(q, "%s", fmtId(opcname));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ANALYZE */
|
||||||
|
analyzeOid = atooid(PQgetvalue(res, 0,
|
||||||
|
PQfnumber(res, "typanalyzeoid")));
|
||||||
|
if (OidIsValid(analyzeOid))
|
||||||
|
appendPQExpBuffer(q, ",\n ANALYZE = %s",
|
||||||
|
PQgetvalue(res, 0, PQfnumber(res, "typanalyze")));
|
||||||
|
|
||||||
|
/* CANONICAL */
|
||||||
|
canonicalOid = atooid(PQgetvalue(res, 0,
|
||||||
|
PQfnumber(res, "rngcanonicaloid")));
|
||||||
|
if (OidIsValid(canonicalOid))
|
||||||
|
appendPQExpBuffer(q, ",\n CANONICAL = %s",
|
||||||
|
PQgetvalue(res, 0, PQfnumber(res, "rngcanonical")));
|
||||||
|
|
||||||
|
/* SUBTYPE_DIFF */
|
||||||
|
subdiffOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "rngsubdiffoid")));
|
||||||
|
if (OidIsValid(subdiffOid))
|
||||||
|
appendPQExpBuffer(q, ",\n SUBTYPE_DIFF = %s",
|
||||||
|
PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff")));
|
||||||
|
|
||||||
|
appendPQExpBuffer(q, "\n);\n");
|
||||||
|
|
||||||
|
appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name));
|
||||||
|
|
||||||
|
if (binary_upgrade)
|
||||||
|
binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data);
|
||||||
|
|
||||||
|
ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
|
||||||
|
tyinfo->dobj.name,
|
||||||
|
tyinfo->dobj.namespace->dobj.name,
|
||||||
|
NULL,
|
||||||
|
tyinfo->rolname, false,
|
||||||
|
"TYPE", SECTION_PRE_DATA,
|
||||||
|
q->data, delq->data, NULL,
|
||||||
|
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
|
||||||
|
NULL, NULL);
|
||||||
|
|
||||||
|
/* Dump Type Comments and Security Labels */
|
||||||
|
dumpComment(fout, labelq->data,
|
||||||
|
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
|
||||||
|
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
|
||||||
|
dumpSecLabel(fout, labelq->data,
|
||||||
|
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
|
||||||
|
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
destroyPQExpBuffer(q);
|
||||||
|
destroyPQExpBuffer(delq);
|
||||||
|
destroyPQExpBuffer(labelq);
|
||||||
|
destroyPQExpBuffer(query);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dumpBaseType
|
* dumpBaseType
|
||||||
* writes out to fout the queries to recreate a user-defined base type
|
* writes out to fout the queries to recreate a user-defined base type
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201110221
|
/* COMMITTER: please set appropriately */
|
||||||
|
#define CATALOG_VERSION_NO 201111111
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -303,6 +303,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(o
|
|||||||
DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops));
|
DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops));
|
||||||
#define ExtensionNameIndexId 3081
|
#define ExtensionNameIndexId 3081
|
||||||
|
|
||||||
|
DECLARE_UNIQUE_INDEX(pg_range_rgntypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
|
||||||
|
#define RangeTypidIndexId 3542
|
||||||
|
|
||||||
/* last step of initialization script: build the indexes declared above */
|
/* last step of initialization script: build the indexes declared above */
|
||||||
BUILD_INDICES
|
BUILD_INDICES
|
||||||
|
|
||||||
|
@ -709,4 +709,34 @@ DATA(insert ( 3683 3615 3615 5 s 3679 403 0 ));
|
|||||||
DATA(insert ( 3702 3615 3615 7 s 3693 783 0 ));
|
DATA(insert ( 3702 3615 3615 7 s 3693 783 0 ));
|
||||||
DATA(insert ( 3702 3615 3615 8 s 3694 783 0 ));
|
DATA(insert ( 3702 3615 3615 8 s 3694 783 0 ));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* btree range_ops
|
||||||
|
*/
|
||||||
|
DATA(insert ( 3901 3831 3831 1 s 3884 403 0 ));
|
||||||
|
DATA(insert ( 3901 3831 3831 2 s 3885 403 0 ));
|
||||||
|
DATA(insert ( 3901 3831 3831 3 s 3882 403 0 ));
|
||||||
|
DATA(insert ( 3901 3831 3831 4 s 3886 403 0 ));
|
||||||
|
DATA(insert ( 3901 3831 3831 5 s 3887 403 0 ));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hash range_ops
|
||||||
|
*/
|
||||||
|
DATA(insert ( 3903 3831 3831 1 s 3882 405 0 ));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GiST range_ops
|
||||||
|
*/
|
||||||
|
DATA(insert ( 3919 3831 3831 1 s 3882 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 2 s 3883 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 3 s 3888 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 2776 4 s 3889 783 0 ));
|
||||||
|
DATA(insert ( 3919 2776 3831 5 s 3891 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 6 s 3890 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 7 s 3892 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 8 s 3893 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 9 s 3894 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 10 s 3895 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 11 s 3896 783 0 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 12 s 3897 783 0 ));
|
||||||
|
|
||||||
#endif /* PG_AMOP_H */
|
#endif /* PG_AMOP_H */
|
||||||
|
@ -336,5 +336,14 @@ DATA(insert ( 3659 3614 3614 4 3658 ));
|
|||||||
DATA(insert ( 3659 3614 3614 5 2700 ));
|
DATA(insert ( 3659 3614 3614 5 2700 ));
|
||||||
DATA(insert ( 3626 3614 3614 1 3622 ));
|
DATA(insert ( 3626 3614 3614 1 3622 ));
|
||||||
DATA(insert ( 3683 3615 3615 1 3668 ));
|
DATA(insert ( 3683 3615 3615 1 3668 ));
|
||||||
|
DATA(insert ( 3901 3831 3831 1 3870 ));
|
||||||
|
DATA(insert ( 3903 3831 3831 1 3902 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 1 3875 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 2 3876 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 3 3877 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 4 3878 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 5 3879 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 6 3880 ));
|
||||||
|
DATA(insert ( 3919 3831 3831 7 3881 ));
|
||||||
|
|
||||||
#endif /* PG_AMPROC_H */
|
#endif /* PG_AMPROC_H */
|
||||||
|
@ -213,5 +213,8 @@ DATA(insert ( 783 tsvector_ops PGNSP PGUID 3655 3614 t 3642 ));
|
|||||||
DATA(insert ( 2742 tsvector_ops PGNSP PGUID 3659 3614 t 25 ));
|
DATA(insert ( 2742 tsvector_ops PGNSP PGUID 3659 3614 t 25 ));
|
||||||
DATA(insert ( 403 tsquery_ops PGNSP PGUID 3683 3615 t 0 ));
|
DATA(insert ( 403 tsquery_ops PGNSP PGUID 3683 3615 t 0 ));
|
||||||
DATA(insert ( 783 tsquery_ops PGNSP PGUID 3702 3615 t 20 ));
|
DATA(insert ( 783 tsquery_ops PGNSP PGUID 3702 3615 t 20 ));
|
||||||
|
DATA(insert ( 403 range_ops PGNSP PGUID 3901 3831 t 0 ));
|
||||||
|
DATA(insert ( 405 range_ops PGNSP PGUID 3903 3831 t 0 ));
|
||||||
|
DATA(insert ( 783 range_ops PGNSP PGUID 3919 3831 t 0 ));
|
||||||
|
|
||||||
#endif /* PG_OPCLASS_H */
|
#endif /* PG_OPCLASS_H */
|
||||||
|
@ -1661,6 +1661,45 @@ DESCR("less than or equal");
|
|||||||
DATA(insert OID = 2993 ( ">=" PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel ));
|
DATA(insert OID = 2993 ( ">=" PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel ));
|
||||||
DESCR("greater than or equal");
|
DESCR("greater than or equal");
|
||||||
|
|
||||||
|
/* generic range type operators */
|
||||||
|
DATA(insert OID = 3882 ( "=" PGNSP PGUID b t t 3831 3831 16 3882 3883 range_eq eqsel eqjoinsel ));
|
||||||
|
DESCR("equal");
|
||||||
|
DATA(insert OID = 3883 ( "<>" PGNSP PGUID b f f 3831 3831 16 3883 3882 range_ne neqsel neqjoinsel ));
|
||||||
|
DESCR("not equal");
|
||||||
|
DATA(insert OID = 3884 ( "<" PGNSP PGUID b f f 3831 3831 16 3887 3886 range_lt scalarltsel scalarltjoinsel ));
|
||||||
|
DESCR("less than");
|
||||||
|
DATA(insert OID = 3885 ( "<=" PGNSP PGUID b f f 3831 3831 16 3886 3887 range_le scalarltsel scalarltjoinsel ));
|
||||||
|
DESCR("less than or equal");
|
||||||
|
DATA(insert OID = 3886 ( ">=" PGNSP PGUID b f f 3831 3831 16 3885 3884 range_ge scalargtsel scalargtjoinsel ));
|
||||||
|
DESCR("greater than or equal");
|
||||||
|
DATA(insert OID = 3887 ( ">" PGNSP PGUID b f f 3831 3831 16 3884 3885 range_gt scalargtsel scalargtjoinsel ));
|
||||||
|
DESCR("greater than");
|
||||||
|
DATA(insert OID = 3888 ( "&&" PGNSP PGUID b f f 3831 3831 16 3888 0 3857 - - ));
|
||||||
|
DESCR("overlaps");
|
||||||
|
DATA(insert OID = 3889 ( "@>" PGNSP PGUID b f f 3831 2776 16 3891 0 3858 - - ));
|
||||||
|
DESCR("contains");
|
||||||
|
DATA(insert OID = 3890 ( "@>" PGNSP PGUID b f f 3831 3831 16 3892 0 3859 - - ));
|
||||||
|
DESCR("contains");
|
||||||
|
DATA(insert OID = 3891 ( "<@" PGNSP PGUID b f f 2776 3831 16 3889 0 3860 - - ));
|
||||||
|
DESCR("contained by");
|
||||||
|
DATA(insert OID = 3892 ( "<@" PGNSP PGUID b f f 3831 3831 16 3890 0 3861 - - ));
|
||||||
|
DESCR("contained by");
|
||||||
|
DATA(insert OID = 3893 ( "<<" PGNSP PGUID b f f 3831 3831 16 0 0 before scalarltsel scalarltjoinsel ));
|
||||||
|
DESCR("left of");
|
||||||
|
DATA(insert OID = 3894 ( ">>" PGNSP PGUID b f f 3831 3831 16 0 0 after scalargtsel scalargtjoinsel ));
|
||||||
|
DESCR("right of");
|
||||||
|
DATA(insert OID = 3895 ( "&<" PGNSP PGUID b f f 3831 3831 16 0 0 overleft scalarltsel scalarltjoinsel ));
|
||||||
|
DESCR("overlaps to left");
|
||||||
|
DATA(insert OID = 3896 ( "&>" PGNSP PGUID b f f 3831 3831 16 0 0 overright scalargtsel scalargtjoinsel ));
|
||||||
|
DESCR("overlaps to right");
|
||||||
|
DATA(insert OID = 3897 ( "-|-" PGNSP PGUID b f f 3831 3831 16 3897 0 adjacent - - ));
|
||||||
|
DESCR("adjacent");
|
||||||
|
DATA(insert OID = 3898 ( "+" PGNSP PGUID b f f 3831 3831 3831 3898 0 range_union - - ));
|
||||||
|
DESCR("range union");
|
||||||
|
DATA(insert OID = 3899 ( "-" PGNSP PGUID b f f 3831 3831 3831 0 0 minus - - ));
|
||||||
|
DESCR("range difference");
|
||||||
|
DATA(insert OID = 3900 ( "*" PGNSP PGUID b f f 3831 3831 3831 3900 0 range_intersect - - ));
|
||||||
|
DESCR("intersection");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* function prototypes
|
* function prototypes
|
||||||
|
@ -139,5 +139,8 @@ DATA(insert OID = 3655 ( 783 tsvector_ops PGNSP PGUID ));
|
|||||||
DATA(insert OID = 3659 ( 2742 tsvector_ops PGNSP PGUID ));
|
DATA(insert OID = 3659 ( 2742 tsvector_ops PGNSP PGUID ));
|
||||||
DATA(insert OID = 3683 ( 403 tsquery_ops PGNSP PGUID ));
|
DATA(insert OID = 3683 ( 403 tsquery_ops PGNSP PGUID ));
|
||||||
DATA(insert OID = 3702 ( 783 tsquery_ops PGNSP PGUID ));
|
DATA(insert OID = 3702 ( 783 tsquery_ops PGNSP PGUID ));
|
||||||
|
DATA(insert OID = 3901 ( 403 range_ops PGNSP PGUID ));
|
||||||
|
DATA(insert OID = 3903 ( 405 range_ops PGNSP PGUID ));
|
||||||
|
DATA(insert OID = 3919 ( 783 range_ops PGNSP PGUID ));
|
||||||
|
|
||||||
#endif /* PG_OPFAMILY_H */
|
#endif /* PG_OPFAMILY_H */
|
||||||
|
@ -4334,6 +4334,153 @@ DESCR("fetch the last row value");
|
|||||||
DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 0 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ ));
|
DATA(insert OID = 3114 ( nth_value PGNSP PGUID 12 1 0 0 0 f t f t f i 2 0 2283 "2283 23" _null_ _null_ _null_ _null_ window_nth_value _null_ _null_ _null_ ));
|
||||||
DESCR("fetch the Nth row value");
|
DESCR("fetch the Nth row value");
|
||||||
|
|
||||||
|
/* procs for range types */
|
||||||
|
DATA(insert OID = 3832 ( anyrange_in PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2275 26 23" _null_ _null_ _null_ _null_ anyrange_in _null_ _null_ _null_ ));
|
||||||
|
DESCR("I/O");
|
||||||
|
DATA(insert OID = 3833 ( anyrange_out PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 2275 "3831" _null_ _null_ _null_ _null_ anyrange_out _null_ _null_ _null_ ));
|
||||||
|
DESCR("I/O");
|
||||||
|
DATA(insert OID = 3834 ( range_in PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2275 26 23" _null_ _null_ _null_ _null_ range_in _null_ _null_ _null_ ));
|
||||||
|
DESCR("I/O");
|
||||||
|
DATA(insert OID = 3835 ( range_out PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 2275 "3831" _null_ _null_ _null_ _null_ range_out _null_ _null_ _null_ ));
|
||||||
|
DESCR("I/O");
|
||||||
|
DATA(insert OID = 3836 ( range_recv PGNSP PGUID 12 1 0 0 0 f f f t f s 3 0 3831 "2281 26 23" _null_ _null_ _null_ _null_ range_recv _null_ _null_ _null_ ));
|
||||||
|
DESCR("I/O");
|
||||||
|
DATA(insert OID = 3837 ( range_send PGNSP PGUID 12 1 0 0 0 f f f t f s 1 0 17 "3831" _null_ _null_ _null_ _null_ range_send _null_ _null_ _null_ ));
|
||||||
|
DESCR("I/O");
|
||||||
|
DATA(insert OID = 3848 ( lower PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ range_lower _null_ _null_ _null_ ));
|
||||||
|
DESCR("return the range's lower bound");
|
||||||
|
DATA(insert OID = 3849 ( upper PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2283 "3831" _null_ _null_ _null_ _null_ range_upper _null_ _null_ _null_ ));
|
||||||
|
DESCR("return the range's upper bound");
|
||||||
|
DATA(insert OID = 3850 ( isempty PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_empty _null_ _null_ _null_ ));
|
||||||
|
DESCR("is the range empty?");
|
||||||
|
DATA(insert OID = 3851 ( lower_inc PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_lower_inc _null_ _null_ _null_ ));
|
||||||
|
DESCR("is the range's lower bound inclusive?");
|
||||||
|
DATA(insert OID = 3852 ( upper_inc PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_upper_inc _null_ _null_ _null_ ));
|
||||||
|
DESCR("is the range's upper bound inclusive?");
|
||||||
|
DATA(insert OID = 3853 ( lower_inf PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_lower_inf _null_ _null_ _null_ ));
|
||||||
|
DESCR("is the range's lower bound infinite?");
|
||||||
|
DATA(insert OID = 3854 ( upper_inf PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 16 "3831" _null_ _null_ _null_ _null_ range_upper_inf _null_ _null_ _null_ ));
|
||||||
|
DESCR("is the range's upper bound infinite?");
|
||||||
|
DATA(insert OID = 3855 ( range_eq PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_eq _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of = operator");
|
||||||
|
DATA(insert OID = 3856 ( range_ne PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_ne _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of <> operator");
|
||||||
|
DATA(insert OID = 3857 ( overlaps PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overlaps _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of && operator");
|
||||||
|
DATA(insert OID = 3858 ( contains PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 2776" _null_ _null_ _null_ _null_ range_contains_elem _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of @> operator");
|
||||||
|
DATA(insert OID = 3859 ( contains PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contains _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of @> operator");
|
||||||
|
DATA(insert OID = 3860 ( contained_by PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "2776 3831" _null_ _null_ _null_ _null_ elem_contained_by_range _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of <@ operator");
|
||||||
|
DATA(insert OID = 3861 ( contained_by PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_contained_by _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of <@ operator");
|
||||||
|
DATA(insert OID = 3862 ( adjacent PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_adjacent _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of -|- operator");
|
||||||
|
DATA(insert OID = 3863 ( before PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_before _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of << operator");
|
||||||
|
DATA(insert OID = 3864 ( after PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_after _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of >> operator");
|
||||||
|
DATA(insert OID = 3865 ( overleft PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overleft _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of &< operator");
|
||||||
|
DATA(insert OID = 3866 ( overright PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_overright _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of &> operator");
|
||||||
|
DATA(insert OID = 3867 ( range_union PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_union _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of + operator");
|
||||||
|
DATA(insert OID = 3868 ( range_intersect PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_intersect _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of * operator");
|
||||||
|
DATA(insert OID = 3869 ( minus PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 3831 "3831 3831" _null_ _null_ _null_ _null_ range_minus _null_ _null_ _null_ ));
|
||||||
|
DESCR("implementation of - operator");
|
||||||
|
DATA(insert OID = 3870 ( range_cmp PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 23 "3831 3831" _null_ _null_ _null_ _null_ range_cmp _null_ _null_ _null_ ));
|
||||||
|
DESCR("less-equal-greater");
|
||||||
|
DATA(insert OID = 3871 ( range_lt PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_lt _null_ _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3872 ( range_le PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_le _null_ _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3873 ( range_ge PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_ge _null_ _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3874 ( range_gt PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "3831 3831" _null_ _null_ _null_ _null_ range_gt _null_ _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3875 ( range_gist_consistent PGNSP PGUID 12 1 0 0 0 f f f t f i 5 0 16 "2281 3831 21 26 2281" _null_ _null_ _null_ _null_ range_gist_consistent _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3876 ( range_gist_union PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ range_gist_union _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3877 ( range_gist_compress PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ range_gist_compress _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3878 ( range_gist_decompress PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ range_gist_decompress _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3879 ( range_gist_penalty PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ range_gist_penalty _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3880 ( range_gist_picksplit PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ range_gist_picksplit _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3881 ( range_gist_same PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ range_gist_same _null_ _null_ _null_ ));
|
||||||
|
DESCR("GiST support");
|
||||||
|
DATA(insert OID = 3902 ( hash_range PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 23 "3831" _null_ _null_ _null_ _null_ hash_range _null_ _null_ _null_ ));
|
||||||
|
DESCR("hash a range");
|
||||||
|
DATA(insert OID = 3914 ( int4range_canonical PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3904 "3904" _null_ _null_ _null_ _null_ int4range_canonical _null_ _null_ _null_ ));
|
||||||
|
DESCR("convert an int4 range to canonical form");
|
||||||
|
DATA(insert OID = 3928 ( int8range_canonical PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3926 "3926" _null_ _null_ _null_ _null_ int8range_canonical _null_ _null_ _null_ ));
|
||||||
|
DESCR("convert an int8 range to canonical form");
|
||||||
|
DATA(insert OID = 3915 ( daterange_canonical PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 3906 "3906" _null_ _null_ _null_ _null_ daterange_canonical _null_ _null_ _null_ ));
|
||||||
|
DESCR("convert a date range to canonical form");
|
||||||
|
DATA(insert OID = 3922 ( int4range_subdiff PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "23 23" _null_ _null_ _null_ _null_ int4range_subdiff _null_ _null_ _null_ ));
|
||||||
|
DESCR("float8 difference of two int4 values");
|
||||||
|
DATA(insert OID = 3923 ( int8range_subdiff PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "20 20" _null_ _null_ _null_ _null_ int8range_subdiff _null_ _null_ _null_ ));
|
||||||
|
DESCR("float8 difference of two int8 values");
|
||||||
|
DATA(insert OID = 3924 ( numrange_subdiff PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "1700 1700" _null_ _null_ _null_ _null_ numrange_subdiff _null_ _null_ _null_ ));
|
||||||
|
DESCR("float8 difference of two numeric values");
|
||||||
|
DATA(insert OID = 3925 ( daterange_subdiff PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "1082 1082" _null_ _null_ _null_ _null_ daterange_subdiff _null_ _null_ _null_ ));
|
||||||
|
DESCR("float8 difference of two date values");
|
||||||
|
DATA(insert OID = 3929 ( tsrange_subdiff PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "1114 1114" _null_ _null_ _null_ _null_ tsrange_subdiff _null_ _null_ _null_ ));
|
||||||
|
DESCR("float8 difference of two timestamp values");
|
||||||
|
DATA(insert OID = 3930 ( tstzrange_subdiff PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 701 "1184 1184" _null_ _null_ _null_ _null_ tstzrange_subdiff _null_ _null_ _null_ ));
|
||||||
|
DESCR("float8 difference of two timestamp with time zone values");
|
||||||
|
|
||||||
|
|
||||||
|
DATA(insert OID = 3838 ( int4range PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3904 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
|
||||||
|
DESCR("int4range constructor");
|
||||||
|
DATA(insert OID = 3839 ( int4range PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3904 "23" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
|
||||||
|
DESCR("int4range constructor");
|
||||||
|
DATA(insert OID = 3840 ( int4range PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3904 "23 23" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
|
||||||
|
DESCR("int4range constructor");
|
||||||
|
DATA(insert OID = 3841 ( int4range PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3904 "23 23 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
|
||||||
|
DESCR("int4range constructor");
|
||||||
|
DATA(insert OID = 3842 ( numrange PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3906 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
|
||||||
|
DESCR("numrange constructor");
|
||||||
|
DATA(insert OID = 3843 ( numrange PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3906 "1700" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
|
||||||
|
DESCR("numrange constructor");
|
||||||
|
DATA(insert OID = 3844 ( numrange PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3906 "1700 1700" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
|
||||||
|
DESCR("numrange constructor");
|
||||||
|
DATA(insert OID = 3845 ( numrange PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3906 "1700 1700 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
|
||||||
|
DESCR("numrange constructor");
|
||||||
|
DATA(insert OID = 3846 ( tsrange PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3908 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
|
||||||
|
DESCR("tsrange constructor");
|
||||||
|
DATA(insert OID = 3847 ( tsrange PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3908 "1114" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
|
||||||
|
DESCR("tsrange constructor");
|
||||||
|
DATA(insert OID = 3933 ( tsrange PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3908 "1114 1114" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
|
||||||
|
DESCR("tsrange constructor");
|
||||||
|
DATA(insert OID = 3934 ( tsrange PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3908 "1114 1114 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
|
||||||
|
DESCR("tsrange constructor");
|
||||||
|
DATA(insert OID = 3935 ( tstzrange PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3910 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
|
||||||
|
DESCR("tstzrange constructor");
|
||||||
|
DATA(insert OID = 3936 ( tstzrange PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3910 "1184" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
|
||||||
|
DESCR("tstzrange constructor");
|
||||||
|
DATA(insert OID = 3937 ( tstzrange PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3910 "1184 1184" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
|
||||||
|
DESCR("tstzrange constructor");
|
||||||
|
DATA(insert OID = 3938 ( tstzrange PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3910 "1184 1184 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
|
||||||
|
DESCR("tstzrange constructor");
|
||||||
|
DATA(insert OID = 3939 ( daterange PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3912 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
|
||||||
|
DESCR("daterange constructor");
|
||||||
|
DATA(insert OID = 3940 ( daterange PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3912 "1082" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
|
||||||
|
DESCR("daterange constructor");
|
||||||
|
DATA(insert OID = 3941 ( daterange PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3912 "1082 1082" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
|
||||||
|
DESCR("daterange constructor");
|
||||||
|
DATA(insert OID = 3942 ( daterange PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3912 "1082 1082 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
|
||||||
|
DESCR("daterange constructor");
|
||||||
|
DATA(insert OID = 3943 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f i 0 0 3926 "" _null_ _null_ _null_ _null_ range_constructor0 _null_ _null_ _null_ ));
|
||||||
|
DESCR("int8range constructor");
|
||||||
|
DATA(insert OID = 3944 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f i 1 0 3926 "20" _null_ _null_ _null_ _null_ range_constructor1 _null_ _null_ _null_ ));
|
||||||
|
DESCR("int8range constructor");
|
||||||
|
DATA(insert OID = 3945 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f i 2 0 3926 "20 20" _null_ _null_ _null_ _null_ range_constructor2 _null_ _null_ _null_ ));
|
||||||
|
DESCR("int8range constructor");
|
||||||
|
DATA(insert OID = 3946 ( int8range PGNSP PGUID 12 1 0 0 0 f f f f f i 3 0 3926 "20 20 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
|
||||||
|
DESCR("int8range constructor");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Symbolic values for provolatile column: these indicate whether the result
|
* Symbolic values for provolatile column: these indicate whether the result
|
||||||
|
84
src/include/catalog/pg_range.h
Normal file
84
src/include/catalog/pg_range.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* pg_range.h
|
||||||
|
* definition of the system "range" relation (pg_range)
|
||||||
|
* along with the relation's initial contents.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006-2010, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* src/include/catalog/pg_range.h
|
||||||
|
*
|
||||||
|
* NOTES
|
||||||
|
* the genbki.pl script reads this file and generates .bki
|
||||||
|
* information from the DATA() statements.
|
||||||
|
*
|
||||||
|
* XXX do NOT break up DATA() statements into multiple lines!
|
||||||
|
* the scripts are not as smart as you might think...
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef PG_RANGE_H
|
||||||
|
#define PG_RANGE_H
|
||||||
|
|
||||||
|
#include "catalog/genbki.h"
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* pg_range definition. cpp turns this into
|
||||||
|
* typedef struct FormData_pg_range
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
#define RangeRelationId 3541
|
||||||
|
|
||||||
|
CATALOG(pg_range,3541) BKI_WITHOUT_OIDS
|
||||||
|
{
|
||||||
|
Oid rngtypid; /* OID of owning range type */
|
||||||
|
Oid rngsubtype; /* OID of range's subtype */
|
||||||
|
Oid rngcollation; /* collation for this range type, or 0 */
|
||||||
|
Oid rngsubopc; /* subtype's btree opclass */
|
||||||
|
regproc rngcanonical; /* canonicalize range, or 0 */
|
||||||
|
regproc rngsubdiff; /* subtype difference as a float8 (for GiST) */
|
||||||
|
} FormData_pg_range;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* Form_pg_range corresponds to a pointer to a tuple with
|
||||||
|
* the format of pg_range relation.
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
typedef FormData_pg_range *Form_pg_range;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* compiler constants for pg_range
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
#define Natts_pg_range 6
|
||||||
|
#define Anum_pg_range_rngtypid 1
|
||||||
|
#define Anum_pg_range_rngsubtype 2
|
||||||
|
#define Anum_pg_range_rngcollation 3
|
||||||
|
#define Anum_pg_range_rngsubopc 4
|
||||||
|
#define Anum_pg_range_rngcanonical 5
|
||||||
|
#define Anum_pg_range_rngsubdiff 6
|
||||||
|
|
||||||
|
#define RANGE_DEFAULT_FLAGS "[)"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prototypes for functions in pg_range.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
|
||||||
|
Oid rangeSubOpclass, RegProcedure rangeCanonical,
|
||||||
|
RegProcedure rangeSubDiff);
|
||||||
|
extern void RangeDelete(Oid rangeTypeOid);
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* initial contents of pg_range
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
DATA(insert ( 3904 23 0 1978 int4range_canonical int4range_subdiff));
|
||||||
|
DATA(insert ( 3906 1700 0 10037 - numrange_subdiff));
|
||||||
|
DATA(insert ( 3908 1114 0 10054 - tsrange_subdiff));
|
||||||
|
DATA(insert ( 3910 1184 0 10047 - tstzrange_subdiff));
|
||||||
|
DATA(insert ( 3912 1082 0 10019 daterange_canonical daterange_subdiff));
|
||||||
|
DATA(insert ( 3926 20 0 10029 int8range_canonical int8range_subdiff));
|
||||||
|
|
||||||
|
#endif /* PG_RANGE_H */
|
@ -591,6 +591,28 @@ DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 tx
|
|||||||
DESCR("txid snapshot");
|
DESCR("txid snapshot");
|
||||||
DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ ));
|
DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
|
||||||
|
/* range types */
|
||||||
|
|
||||||
|
DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DESCR("range of int4s");
|
||||||
|
#define INT4RANGEOID 3904
|
||||||
|
DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DESCR("range of numerics");
|
||||||
|
DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DESCR("range of timestamps");
|
||||||
|
DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DESCR("range of timestamps with time zone");
|
||||||
|
DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DESCR("range of dates");
|
||||||
|
DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
DESCR("range of int8s");
|
||||||
|
DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pseudo-types
|
* pseudo-types
|
||||||
*
|
*
|
||||||
@ -632,6 +654,8 @@ DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in
|
|||||||
#define ANYENUMOID 3500
|
#define ANYENUMOID 3500
|
||||||
DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ ));
|
DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ ));
|
||||||
#define FDW_HANDLEROID 3115
|
#define FDW_HANDLEROID 3115
|
||||||
|
DATA(insert OID = 3831 ( anyrange PGNSP PGUID 4 t p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - i p f 0 -1 0 0 _null_ _null_ ));
|
||||||
|
#define ANYRANGEOID 3831
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -642,6 +666,7 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
|
|||||||
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
|
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
|
||||||
#define TYPTYPE_ENUM 'e' /* enumerated type */
|
#define TYPTYPE_ENUM 'e' /* enumerated type */
|
||||||
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
|
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
|
||||||
|
#define TYPTYPE_RANGE 'r' /* range type */
|
||||||
|
|
||||||
#define TYPCATEGORY_INVALID '\0' /* not an allowed category */
|
#define TYPCATEGORY_INVALID '\0' /* not an allowed category */
|
||||||
#define TYPCATEGORY_ARRAY 'A'
|
#define TYPCATEGORY_ARRAY 'A'
|
||||||
@ -653,6 +678,7 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
|
|||||||
#define TYPCATEGORY_NETWORK 'I' /* think INET */
|
#define TYPCATEGORY_NETWORK 'I' /* think INET */
|
||||||
#define TYPCATEGORY_NUMERIC 'N'
|
#define TYPCATEGORY_NUMERIC 'N'
|
||||||
#define TYPCATEGORY_PSEUDOTYPE 'P'
|
#define TYPCATEGORY_PSEUDOTYPE 'P'
|
||||||
|
#define TYPCATEGORY_RANGE 'R'
|
||||||
#define TYPCATEGORY_STRING 'S'
|
#define TYPCATEGORY_STRING 'S'
|
||||||
#define TYPCATEGORY_TIMESPAN 'T'
|
#define TYPCATEGORY_TIMESPAN 'T'
|
||||||
#define TYPCATEGORY_USER 'U'
|
#define TYPCATEGORY_USER 'U'
|
||||||
@ -664,6 +690,7 @@ DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_han
|
|||||||
((typid) == ANYELEMENTOID || \
|
((typid) == ANYELEMENTOID || \
|
||||||
(typid) == ANYARRAYOID || \
|
(typid) == ANYARRAYOID || \
|
||||||
(typid) == ANYNONARRAYOID || \
|
(typid) == ANYNONARRAYOID || \
|
||||||
(typid) == ANYENUMOID)
|
(typid) == ANYENUMOID || \
|
||||||
|
(typid) == ANYRANGEOID)
|
||||||
|
|
||||||
#endif /* PG_TYPE_H */
|
#endif /* PG_TYPE_H */
|
||||||
|
@ -23,6 +23,7 @@ extern void DefineType(List *names, List *parameters);
|
|||||||
extern void RemoveTypeById(Oid typeOid);
|
extern void RemoveTypeById(Oid typeOid);
|
||||||
extern void DefineDomain(CreateDomainStmt *stmt);
|
extern void DefineDomain(CreateDomainStmt *stmt);
|
||||||
extern void DefineEnum(CreateEnumStmt *stmt);
|
extern void DefineEnum(CreateEnumStmt *stmt);
|
||||||
|
extern void DefineRange(CreateRangeStmt *stmt);
|
||||||
extern void AlterEnum(AlterEnumStmt *stmt);
|
extern void AlterEnum(AlterEnumStmt *stmt);
|
||||||
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
|
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
|
||||||
extern Oid AssignTypeArrayOid(void);
|
extern Oid AssignTypeArrayOid(void);
|
||||||
|
@ -346,6 +346,7 @@ typedef enum NodeTag
|
|||||||
T_ReassignOwnedStmt,
|
T_ReassignOwnedStmt,
|
||||||
T_CompositeTypeStmt,
|
T_CompositeTypeStmt,
|
||||||
T_CreateEnumStmt,
|
T_CreateEnumStmt,
|
||||||
|
T_CreateRangeStmt,
|
||||||
T_AlterEnumStmt,
|
T_AlterEnumStmt,
|
||||||
T_AlterTSDictionaryStmt,
|
T_AlterTSDictionaryStmt,
|
||||||
T_AlterTSConfigurationStmt,
|
T_AlterTSConfigurationStmt,
|
||||||
|
@ -2334,6 +2334,17 @@ typedef struct AlterEnumStmt
|
|||||||
bool newValIsAfter; /* place new enum value after neighbor? */
|
bool newValIsAfter; /* place new enum value after neighbor? */
|
||||||
} AlterEnumStmt;
|
} AlterEnumStmt;
|
||||||
|
|
||||||
|
/* ----------------------
|
||||||
|
* Create Type Statement, range types
|
||||||
|
* ----------------------
|
||||||
|
*/
|
||||||
|
typedef struct CreateRangeStmt
|
||||||
|
{
|
||||||
|
NodeTag type;
|
||||||
|
List *typeName; /* qualified name (list of Value strings) */
|
||||||
|
List *params; /* range parameters (list of DefElem) */
|
||||||
|
} CreateRangeStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
* Create View Statement
|
* Create View Statement
|
||||||
* ----------------------
|
* ----------------------
|
||||||
|
@ -119,6 +119,7 @@ extern Node *get_typdefault(Oid typid);
|
|||||||
extern char get_typtype(Oid typid);
|
extern char get_typtype(Oid typid);
|
||||||
extern bool type_is_rowtype(Oid typid);
|
extern bool type_is_rowtype(Oid typid);
|
||||||
extern bool type_is_enum(Oid typid);
|
extern bool type_is_enum(Oid typid);
|
||||||
|
extern bool type_is_range(Oid typid);
|
||||||
extern void get_type_category_preferred(Oid typid,
|
extern void get_type_category_preferred(Oid typid,
|
||||||
char *typcategory,
|
char *typcategory,
|
||||||
bool *typispreferred);
|
bool *typispreferred);
|
||||||
@ -147,6 +148,7 @@ extern void free_attstatsslot(Oid atttype,
|
|||||||
Datum *values, int nvalues,
|
Datum *values, int nvalues,
|
||||||
float4 *numbers, int nnumbers);
|
float4 *numbers, int nnumbers);
|
||||||
extern char *get_namespace_name(Oid nspid);
|
extern char *get_namespace_name(Oid nspid);
|
||||||
|
extern Oid get_range_subtype(Oid rangeOid);
|
||||||
|
|
||||||
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
|
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
|
||||||
/* type_is_array_domain accepts both plain arrays and domains over arrays */
|
/* type_is_array_domain accepts both plain arrays and domains over arrays */
|
||||||
|
159
src/include/utils/rangetypes.h
Normal file
159
src/include/utils/rangetypes.h
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* rangetypes.h
|
||||||
|
* Declarations for Postgres range types.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RANGETYPES_H
|
||||||
|
#define RANGETYPES_H
|
||||||
|
|
||||||
|
#include "fmgr.h"
|
||||||
|
|
||||||
|
typedef struct varlena RangeType;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Datum val;
|
||||||
|
Oid rngtypid;
|
||||||
|
bool infinite;
|
||||||
|
bool lower;
|
||||||
|
bool inclusive;
|
||||||
|
} RangeBound;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
FmgrInfo canonicalFn;
|
||||||
|
FmgrInfo cmpFn;
|
||||||
|
FmgrInfo subdiffFn;
|
||||||
|
Oid rngtypid;
|
||||||
|
Oid subtype;
|
||||||
|
Oid collation;
|
||||||
|
int16 subtyplen;
|
||||||
|
char subtypalign;
|
||||||
|
char subtypstorage;
|
||||||
|
bool subtypbyval;
|
||||||
|
} RangeTypeInfo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fmgr macros for range type objects
|
||||||
|
*/
|
||||||
|
#define DatumGetRangeType(X) ((RangeType *) PG_DETOAST_DATUM(X))
|
||||||
|
#define DatumGetRangeTypeCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
|
||||||
|
#define RangeTypeGetDatum(X) PointerGetDatum(X)
|
||||||
|
#define PG_GETARG_RANGE(n) DatumGetRangeType(PG_GETARG_DATUM(n))
|
||||||
|
#define PG_GETARG_RANGE_COPY(n) DatumGetRangeTypeCopy(PG_GETARG_DATUM(n))
|
||||||
|
#define PG_RETURN_RANGE(x) return RangeTypeGetDatum(x)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prototypes for functions defined in rangetypes.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* IO */
|
||||||
|
extern Datum anyrange_in(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum anyrange_out(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_in(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_out(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_recv(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_send(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* constructors */
|
||||||
|
extern Datum range_constructor0(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_constructor1(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_constructor2(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_constructor3(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_make1(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_linf_(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_uinf_(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_linfi(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_uinfi(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range__(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_i(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum rangei_(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum rangeii(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* range -> subtype */
|
||||||
|
extern Datum range_lower(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_upper(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* range -> bool */
|
||||||
|
extern Datum range_empty(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_lower_inc(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_upper_inc(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_lower_inf(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_upper_inf(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* range, point -> bool */
|
||||||
|
extern Datum range_contains_elem(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum elem_contained_by_range(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* range, range -> bool */
|
||||||
|
extern Datum range_eq(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_ne(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_contains(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_contained_by(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_before(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_after(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_adjacent(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_overlaps(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_overleft(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_overright(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* range, range -> range */
|
||||||
|
extern Datum range_minus(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_union(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_intersect(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* BTree support */
|
||||||
|
extern Datum range_cmp(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_lt(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_le(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_ge(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_gt(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* Hash support */
|
||||||
|
extern Datum hash_range(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* GiST support (rangetypes_gist.c) */
|
||||||
|
extern Datum range_gist_consistent(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_gist_compress(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_gist_decompress(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_gist_union(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_gist_penalty(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_gist_picksplit(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum range_gist_same(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* Canonical functions */
|
||||||
|
Datum int4range_canonical(PG_FUNCTION_ARGS);
|
||||||
|
Datum int8range_canonical(PG_FUNCTION_ARGS);
|
||||||
|
Datum daterange_canonical(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* Subtype Difference functions */
|
||||||
|
Datum int4range_subdiff(PG_FUNCTION_ARGS);
|
||||||
|
Datum int8range_subdiff(PG_FUNCTION_ARGS);
|
||||||
|
Datum numrange_subdiff(PG_FUNCTION_ARGS);
|
||||||
|
Datum daterange_subdiff(PG_FUNCTION_ARGS);
|
||||||
|
Datum tsrange_subdiff(PG_FUNCTION_ARGS);
|
||||||
|
Datum tstzrange_subdiff(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* for defining more generic functions */
|
||||||
|
extern Datum make_range(FunctionCallInfo fcinfo, RangeBound *lower,
|
||||||
|
RangeBound *upper, bool empty);
|
||||||
|
extern void range_deserialize(FunctionCallInfo fcinfo, RangeType *range,
|
||||||
|
RangeBound *lower, RangeBound *upper,
|
||||||
|
bool *empty);
|
||||||
|
extern int range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1,
|
||||||
|
RangeBound *b2);
|
||||||
|
extern RangeType *make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid);
|
||||||
|
extern void range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid,
|
||||||
|
RangeTypeInfo *rngtypinfo);
|
||||||
|
|
||||||
|
/* for defining a range "canonicalize" function */
|
||||||
|
extern Datum range_serialize(FunctionCallInfo fcinfo, RangeBound *lower,
|
||||||
|
RangeBound *upper, bool empty);
|
||||||
|
|
||||||
|
/* for use in DefineRange */
|
||||||
|
extern char range_parse_flags(char *flags_str);
|
||||||
|
|
||||||
|
#endif /* RANGETYPES_H */
|
@ -70,6 +70,7 @@ enum SysCacheIdentifier
|
|||||||
OPFAMILYOID,
|
OPFAMILYOID,
|
||||||
PROCNAMEARGSNSP,
|
PROCNAMEARGSNSP,
|
||||||
PROCOID,
|
PROCOID,
|
||||||
|
RANGETYPE,
|
||||||
RELNAMENSP,
|
RELNAMENSP,
|
||||||
RELOID,
|
RELOID,
|
||||||
RULERELNAME,
|
RULERELNAME,
|
||||||
|
@ -490,6 +490,8 @@ do_compile(FunctionCallInfo fcinfo,
|
|||||||
{
|
{
|
||||||
if (rettypeid == ANYARRAYOID)
|
if (rettypeid == ANYARRAYOID)
|
||||||
rettypeid = INT4ARRAYOID;
|
rettypeid = INT4ARRAYOID;
|
||||||
|
else if (rettypeid == ANYRANGEOID)
|
||||||
|
rettypeid = INT4RANGEOID;
|
||||||
else /* ANYELEMENT or ANYNONARRAY */
|
else /* ANYELEMENT or ANYNONARRAY */
|
||||||
rettypeid = INT4OID;
|
rettypeid = INT4OID;
|
||||||
/* XXX what could we use for ANYENUM? */
|
/* XXX what could we use for ANYENUM? */
|
||||||
@ -2119,6 +2121,7 @@ build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
|
|||||||
case TYPTYPE_BASE:
|
case TYPTYPE_BASE:
|
||||||
case TYPTYPE_DOMAIN:
|
case TYPTYPE_DOMAIN:
|
||||||
case TYPTYPE_ENUM:
|
case TYPTYPE_ENUM:
|
||||||
|
case TYPTYPE_RANGE:
|
||||||
typ->ttype = PLPGSQL_TTYPE_SCALAR;
|
typ->ttype = PLPGSQL_TTYPE_SCALAR;
|
||||||
break;
|
break;
|
||||||
case TYPTYPE_COMPOSITE:
|
case TYPTYPE_COMPOSITE:
|
||||||
@ -2373,8 +2376,8 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
|
|||||||
/*
|
/*
|
||||||
* This is the same as the standard resolve_polymorphic_argtypes() function,
|
* This is the same as the standard resolve_polymorphic_argtypes() function,
|
||||||
* but with a special case for validation: assume that polymorphic arguments
|
* but with a special case for validation: assume that polymorphic arguments
|
||||||
* are integer or integer-array. Also, we go ahead and report the error
|
* are integer, integer-range or integer-array. Also, we go ahead and report
|
||||||
* if we can't resolve the types.
|
* the error if we can't resolve the types.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
plpgsql_resolve_polymorphic_argtypes(int numargs,
|
plpgsql_resolve_polymorphic_argtypes(int numargs,
|
||||||
@ -2407,6 +2410,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
|
|||||||
case ANYENUMOID: /* XXX dubious */
|
case ANYENUMOID: /* XXX dubious */
|
||||||
argtypes[i] = INT4OID;
|
argtypes[i] = INT4OID;
|
||||||
break;
|
break;
|
||||||
|
case ANYRANGEOID:
|
||||||
|
argtypes[i] = INT4RANGEOID;
|
||||||
|
break;
|
||||||
case ANYARRAYOID:
|
case ANYARRAYOID:
|
||||||
argtypes[i] = INT4ARRAYOID;
|
argtypes[i] = INT4ARRAYOID;
|
||||||
break;
|
break;
|
||||||
|
@ -1049,3 +1049,20 @@ Composite type "public.collate_dep_test2"
|
|||||||
|
|
||||||
DROP TABLE collate_dep_test1, collate_dep_test4t;
|
DROP TABLE collate_dep_test1, collate_dep_test4t;
|
||||||
DROP TYPE collate_dep_test2;
|
DROP TYPE collate_dep_test2;
|
||||||
|
-- test range types and collations
|
||||||
|
create type textrange_c as range(subtype=text, collation="C");
|
||||||
|
create type textrange_en_us as range(subtype=text, collation="en_US");
|
||||||
|
select textrange_c('A','Z') @> 'b'::text;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select textrange_en_us('A','Z') @> 'b'::text;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop type textrange_c;
|
||||||
|
drop type textrange_en_us;
|
||||||
|
@ -147,7 +147,9 @@ WHERE p1.oid != p2.oid AND
|
|||||||
p1.prosrc = p2.prosrc AND
|
p1.prosrc = p2.prosrc AND
|
||||||
p1.prolang = 12 AND p2.prolang = 12 AND
|
p1.prolang = 12 AND p2.prolang = 12 AND
|
||||||
NOT p1.proisagg AND NOT p2.proisagg AND
|
NOT p1.proisagg AND NOT p2.proisagg AND
|
||||||
(p1.prorettype < p2.prorettype)
|
(p1.prorettype < p2.prorettype) AND
|
||||||
|
-- range constructor functions are shared by all range types.
|
||||||
|
NOT p1.prosrc LIKE 'range_constructor%'
|
||||||
ORDER BY 1, 2;
|
ORDER BY 1, 2;
|
||||||
prorettype | prorettype
|
prorettype | prorettype
|
||||||
------------+------------
|
------------+------------
|
||||||
@ -161,7 +163,9 @@ WHERE p1.oid != p2.oid AND
|
|||||||
p1.prosrc = p2.prosrc AND
|
p1.prosrc = p2.prosrc AND
|
||||||
p1.prolang = 12 AND p2.prolang = 12 AND
|
p1.prolang = 12 AND p2.prolang = 12 AND
|
||||||
NOT p1.proisagg AND NOT p2.proisagg AND
|
NOT p1.proisagg AND NOT p2.proisagg AND
|
||||||
(p1.proargtypes[0] < p2.proargtypes[0])
|
(p1.proargtypes[0] < p2.proargtypes[0]) AND
|
||||||
|
-- range constructor functions are shared by all range types.
|
||||||
|
NOT p1.prosrc LIKE 'range_constructor%'
|
||||||
ORDER BY 1, 2;
|
ORDER BY 1, 2;
|
||||||
proargtypes | proargtypes
|
proargtypes | proargtypes
|
||||||
-------------+-------------
|
-------------+-------------
|
||||||
@ -178,7 +182,9 @@ WHERE p1.oid != p2.oid AND
|
|||||||
p1.prosrc = p2.prosrc AND
|
p1.prosrc = p2.prosrc AND
|
||||||
p1.prolang = 12 AND p2.prolang = 12 AND
|
p1.prolang = 12 AND p2.prolang = 12 AND
|
||||||
NOT p1.proisagg AND NOT p2.proisagg AND
|
NOT p1.proisagg AND NOT p2.proisagg AND
|
||||||
(p1.proargtypes[1] < p2.proargtypes[1])
|
(p1.proargtypes[1] < p2.proargtypes[1]) AND
|
||||||
|
-- range constructor functions are shared by all range types.
|
||||||
|
NOT p1.prosrc LIKE 'range_constructor%'
|
||||||
ORDER BY 1, 2;
|
ORDER BY 1, 2;
|
||||||
proargtypes | proargtypes
|
proargtypes | proargtypes
|
||||||
-------------+-------------
|
-------------+-------------
|
||||||
@ -1015,19 +1021,30 @@ ORDER BY 1, 2, 3;
|
|||||||
403 | 5 | ~>~
|
403 | 5 | ~>~
|
||||||
405 | 1 | =
|
405 | 1 | =
|
||||||
783 | 1 | <<
|
783 | 1 | <<
|
||||||
|
783 | 1 | =
|
||||||
783 | 1 | @@
|
783 | 1 | @@
|
||||||
783 | 2 | &<
|
783 | 2 | &<
|
||||||
|
783 | 2 | <>
|
||||||
783 | 3 | &&
|
783 | 3 | &&
|
||||||
783 | 4 | &>
|
783 | 4 | &>
|
||||||
|
783 | 4 | @>
|
||||||
|
783 | 5 | <@
|
||||||
783 | 5 | >>
|
783 | 5 | >>
|
||||||
|
783 | 6 | @>
|
||||||
783 | 6 | ~=
|
783 | 6 | ~=
|
||||||
|
783 | 7 | <@
|
||||||
783 | 7 | @>
|
783 | 7 | @>
|
||||||
|
783 | 8 | <<
|
||||||
783 | 8 | <@
|
783 | 8 | <@
|
||||||
783 | 9 | &<|
|
783 | 9 | &<|
|
||||||
|
783 | 9 | >>
|
||||||
|
783 | 10 | &<
|
||||||
783 | 10 | <<|
|
783 | 10 | <<|
|
||||||
783 | 10 | <^
|
783 | 10 | <^
|
||||||
|
783 | 11 | &>
|
||||||
783 | 11 | >^
|
783 | 11 | >^
|
||||||
783 | 11 | |>>
|
783 | 11 | |>>
|
||||||
|
783 | 12 | -|-
|
||||||
783 | 12 | |&>
|
783 | 12 | |&>
|
||||||
783 | 13 | ~
|
783 | 13 | ~
|
||||||
783 | 14 | @
|
783 | 14 | @
|
||||||
@ -1044,7 +1061,7 @@ ORDER BY 1, 2, 3;
|
|||||||
2742 | 2 | @@@
|
2742 | 2 | @@@
|
||||||
2742 | 3 | <@
|
2742 | 3 | <@
|
||||||
2742 | 4 | =
|
2742 | 4 | =
|
||||||
(40 rows)
|
(51 rows)
|
||||||
|
|
||||||
-- Check that all opclass search operators have selectivity estimators.
|
-- Check that all opclass search operators have selectivity estimators.
|
||||||
-- This is not absolutely required, but it seems a reasonable thing
|
-- This is not absolutely required, but it seems a reasonable thing
|
||||||
@ -1053,9 +1070,15 @@ SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.oprname
|
|||||||
FROM pg_amop AS p1, pg_operator AS p2
|
FROM pg_amop AS p1, pg_operator AS p2
|
||||||
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND
|
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND
|
||||||
(p2.oprrest = 0 OR p2.oprjoin = 0);
|
(p2.oprrest = 0 OR p2.oprjoin = 0);
|
||||||
amopfamily | amopopr | oid | oprname
|
amopfamily | amopopr | oid | oprname
|
||||||
------------+---------+-----+---------
|
------------+---------+------+---------
|
||||||
(0 rows)
|
3919 | 3888 | 3888 | &&
|
||||||
|
3919 | 3889 | 3889 | @>
|
||||||
|
3919 | 3891 | 3891 | <@
|
||||||
|
3919 | 3890 | 3890 | @>
|
||||||
|
3919 | 3892 | 3892 | <@
|
||||||
|
3919 | 3897 | 3897 | -|-
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
-- Check that each opclass in an opfamily has associated operators, that is
|
-- Check that each opclass in an opfamily has associated operators, that is
|
||||||
-- ones whose oprleft matches opcintype (possibly by coercion).
|
-- ones whose oprleft matches opcintype (possibly by coercion).
|
||||||
|
@ -4571,3 +4571,17 @@ ERROR: value for domain orderedarray violates check constraint "sorted"
|
|||||||
CONTEXT: PL/pgSQL function "testoa" line 5 at assignment
|
CONTEXT: PL/pgSQL function "testoa" line 5 at assignment
|
||||||
drop function arrayassign1();
|
drop function arrayassign1();
|
||||||
drop function testoa(x1 int, x2 int, x3 int);
|
drop function testoa(x1 int, x2 int, x3 int);
|
||||||
|
-- Test resolve_polymorphic_argtypes() codepath. It is only taken when
|
||||||
|
-- a function is invoked from a different backend from where it's defined,
|
||||||
|
-- so we create the a function with polymorphic argument, reconnect, and
|
||||||
|
-- and then call it.
|
||||||
|
create function rangetypes_plpgsql(out a anyelement, b anyrange, c anyarray)
|
||||||
|
language plpgsql as
|
||||||
|
$$ begin a := upper(b) + c[1]; return; end; $$;
|
||||||
|
\c -
|
||||||
|
select rangetypes_plpgsql(int4range(1,10),ARRAY[2,20]);
|
||||||
|
rangetypes_plpgsql
|
||||||
|
--------------------
|
||||||
|
12
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
951
src/test/regress/expected/rangetypes.out
Normal file
951
src/test/regress/expected/rangetypes.out
Normal file
@ -0,0 +1,951 @@
|
|||||||
|
--
|
||||||
|
-- test parser
|
||||||
|
--
|
||||||
|
create type textrange as range (subtype=text, collation="C");
|
||||||
|
-- negative tests; should fail
|
||||||
|
select ''::textrange;
|
||||||
|
ERROR: malformed range literal: ""
|
||||||
|
LINE 1: select ''::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Missing left parenthesis or bracket.
|
||||||
|
select '-[a,z)'::textrange;
|
||||||
|
ERROR: malformed range literal: "-[a,z)"
|
||||||
|
LINE 1: select '-[a,z)'::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Missing left parenthesis or bracket.
|
||||||
|
select '[a,z) - '::textrange;
|
||||||
|
ERROR: malformed range literal: "[a,z) - "
|
||||||
|
LINE 1: select '[a,z) - '::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Junk after right parenthesis or bracket.
|
||||||
|
select '(",a)'::textrange;
|
||||||
|
ERROR: malformed range literal: "(",a)"
|
||||||
|
LINE 1: select '(",a)'::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Unexpected end of input.
|
||||||
|
select '(,,a)'::textrange;
|
||||||
|
ERROR: malformed range literal: "(,,a)"
|
||||||
|
LINE 1: select '(,,a)'::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Too many boundaries.
|
||||||
|
select '(),a)'::textrange;
|
||||||
|
ERROR: malformed range literal: "(),a)"
|
||||||
|
LINE 1: select '(),a)'::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Missing upper bound.
|
||||||
|
select '(a,))'::textrange;
|
||||||
|
ERROR: malformed range literal: "(a,))"
|
||||||
|
LINE 1: select '(a,))'::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Junk after right parenthesis or bracket.
|
||||||
|
select '(],a)'::textrange;
|
||||||
|
ERROR: malformed range literal: "(],a)"
|
||||||
|
LINE 1: select '(],a)'::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Missing upper bound.
|
||||||
|
select '(a,])'::textrange;
|
||||||
|
ERROR: malformed range literal: "(a,])"
|
||||||
|
LINE 1: select '(a,])'::textrange;
|
||||||
|
^
|
||||||
|
DETAIL: Junk after right parenthesis or bracket.
|
||||||
|
-- should succeed
|
||||||
|
select ' empty '::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
empty
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select ' ( empty, empty ) '::textrange;
|
||||||
|
textrange
|
||||||
|
----------------------
|
||||||
|
(" empty"," empty ")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select ' ( " a " " a ", " z " " z " ) '::textrange;
|
||||||
|
textrange
|
||||||
|
--------------------------
|
||||||
|
(" a a "," z z ")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '(,z)'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
(,z)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '(a,)'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
(a,)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[,z]'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
(,z]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[a,]'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
[a,)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '( , )'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
(" "," ")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '("","")'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
("","")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '["",""]'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
["",""]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '(",",",")'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
(",",",")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '("\\","\\")'::textrange
|
||||||
|
select '(\\,a)'::textrange;
|
||||||
|
ERROR: syntax error at or near "select"
|
||||||
|
LINE 2: select '(\\,a)'::textrange;
|
||||||
|
^
|
||||||
|
select '((,z)'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
("(",z)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '([,z)'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
("[",z)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '(!,()'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
(!,"(")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '(!,[)'::textrange;
|
||||||
|
textrange
|
||||||
|
-----------
|
||||||
|
(!,"[")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop type textrange;
|
||||||
|
--
|
||||||
|
-- create some test data and test the operators
|
||||||
|
--
|
||||||
|
CREATE TABLE numrange_test (nr NUMRANGE);
|
||||||
|
create index numrange_test_btree on numrange_test(nr);
|
||||||
|
SET enable_seqscan = f;
|
||||||
|
INSERT INTO numrange_test VALUES('[,)');
|
||||||
|
INSERT INTO numrange_test VALUES('[3,]');
|
||||||
|
INSERT INTO numrange_test VALUES('[, 5)');
|
||||||
|
INSERT INTO numrange_test VALUES(numrange(1.1, 2.2));
|
||||||
|
INSERT INTO numrange_test VALUES('empty');
|
||||||
|
INSERT INTO numrange_test VALUES(numrange(1.7));
|
||||||
|
SELECT isempty(nr) FROM numrange_test;
|
||||||
|
isempty
|
||||||
|
---------
|
||||||
|
f
|
||||||
|
f
|
||||||
|
f
|
||||||
|
f
|
||||||
|
t
|
||||||
|
f
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test
|
||||||
|
WHERE NOT isempty(nr) AND NOT lower_inf(nr) AND NOT upper_inf(nr);
|
||||||
|
lower_inc | lower | upper | upper_inc
|
||||||
|
-----------+-------+-------+-----------
|
||||||
|
t | 1.1 | 2.2 | f
|
||||||
|
t | 1.7 | 1.7 | t
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM numrange_test WHERE contains(nr, numrange(1.9,1.91));
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
(,)
|
||||||
|
(,5)
|
||||||
|
[1.1,2.2)
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM numrange_test WHERE nr @> numrange(1.0,10000.1);
|
||||||
|
nr
|
||||||
|
-----
|
||||||
|
(,)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM numrange_test WHERE contained_by(numrange(-1e7,-10000.1), nr);
|
||||||
|
nr
|
||||||
|
------
|
||||||
|
(,)
|
||||||
|
(,5)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM numrange_test WHERE 1.9 <@ nr;
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
(,)
|
||||||
|
(,5)
|
||||||
|
[1.1,2.2)
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM numrange_test WHERE nr = 'empty';
|
||||||
|
nr
|
||||||
|
-------
|
||||||
|
empty
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)');
|
||||||
|
nr
|
||||||
|
----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)';
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
[1.1,2.2)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(2.0, 1.0);
|
||||||
|
ERROR: range lower bound must be less than or equal to range upper bound
|
||||||
|
select numrange(2.0, 3.0) -|- numrange(3.0, 4.0);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0));
|
||||||
|
adjacent
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()');
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.0, 2.0) -|- numrange(2.0, 3.0,'[]');
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]'));
|
||||||
|
adjacent
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.1, 3.3) <@ numrange(0.1,10.1);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(0.1, 10.1) <@ numrange(1.1,3.3);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.1, 2.2) - numrange(2.0, 3.0);
|
||||||
|
?column?
|
||||||
|
-----------
|
||||||
|
[1.1,2.0)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.1, 2.2) - numrange(2.2, 3.0);
|
||||||
|
?column?
|
||||||
|
-----------
|
||||||
|
[1.1,2.2)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.1, 2.2,'[]') - numrange(2.0, 3.0);
|
||||||
|
?column?
|
||||||
|
-----------
|
||||||
|
[1.1,2.0)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select minus(numrange(10.1,12.2,'[]'), numrange(110.0,120.2,'(]'));
|
||||||
|
minus
|
||||||
|
-------------
|
||||||
|
[10.1,12.2]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select minus(numrange(10.1,12.2,'[]'), numrange(0.0,120.2,'(]'));
|
||||||
|
minus
|
||||||
|
-------
|
||||||
|
empty
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.0, 2.0) << numrange(3.0, 4.0);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.0, 2.0) >> numrange(3.0, 4.0);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(3.0, 70.0) &< numrange(6.6, 100.0);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.1, 2.2) < numrange(1.0, 200.2);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.1, 2.2) < numrange(1.1, 1.2);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.0, 2.0) + numrange(2.0, 3.0);
|
||||||
|
?column?
|
||||||
|
-----------
|
||||||
|
[1.0,3.0)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.0, 2.0) + numrange(1.5, 3.0);
|
||||||
|
?column?
|
||||||
|
-----------
|
||||||
|
[1.0,3.0)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.0, 2.0) + numrange(2.5, 3.0);
|
||||||
|
ERROR: result range is not contiguous
|
||||||
|
select numrange(1.0, 2.0) * numrange(2.0, 3.0);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
empty
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
|
||||||
|
?column?
|
||||||
|
-----------
|
||||||
|
[1.5,2.0)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
empty
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
|
||||||
|
nr
|
||||||
|
-------
|
||||||
|
(,)
|
||||||
|
(,5)
|
||||||
|
empty
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
|
||||||
|
nr
|
||||||
|
-------
|
||||||
|
(,)
|
||||||
|
(,5)
|
||||||
|
empty
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
(,)
|
||||||
|
[3,)
|
||||||
|
(,5)
|
||||||
|
[1.1,2.2)
|
||||||
|
empty
|
||||||
|
[1.7,1.7]
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
[3,)
|
||||||
|
[1.1,2.2)
|
||||||
|
[1.7,1.7]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
[3,)
|
||||||
|
[1.1,2.2)
|
||||||
|
[1.7,1.7]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
|
||||||
|
nr
|
||||||
|
----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
create table numrange_test2(nr numrange);
|
||||||
|
create index numrange_test2_hash_idx on numrange_test2 (nr);
|
||||||
|
INSERT INTO numrange_test2 VALUES('[, 5)');
|
||||||
|
INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
|
||||||
|
INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
|
||||||
|
INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2,'()'));
|
||||||
|
INSERT INTO numrange_test2 VALUES('empty');
|
||||||
|
select * from numrange_test2 where nr = 'empty'::numrange;
|
||||||
|
nr
|
||||||
|
-------
|
||||||
|
empty
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select * from numrange_test2 where nr = numrange(1.1, 2.2);
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
[1.1,2.2)
|
||||||
|
[1.1,2.2)
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
select * from numrange_test2 where nr = numrange(1.1, 2.3);
|
||||||
|
nr
|
||||||
|
----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
set enable_nestloop=t;
|
||||||
|
set enable_hashjoin=f;
|
||||||
|
set enable_mergejoin=f;
|
||||||
|
select * from numrange_test natural join numrange_test2 order by nr;
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
empty
|
||||||
|
(,5)
|
||||||
|
[1.1,2.2)
|
||||||
|
[1.1,2.2)
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
set enable_nestloop=f;
|
||||||
|
set enable_hashjoin=t;
|
||||||
|
set enable_mergejoin=f;
|
||||||
|
select * from numrange_test natural join numrange_test2 order by nr;
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
empty
|
||||||
|
(,5)
|
||||||
|
[1.1,2.2)
|
||||||
|
[1.1,2.2)
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
set enable_nestloop=f;
|
||||||
|
set enable_hashjoin=f;
|
||||||
|
set enable_mergejoin=t;
|
||||||
|
select * from numrange_test natural join numrange_test2 order by nr;
|
||||||
|
nr
|
||||||
|
-----------
|
||||||
|
empty
|
||||||
|
(,5)
|
||||||
|
[1.1,2.2)
|
||||||
|
[1.1,2.2)
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
set enable_nestloop to default;
|
||||||
|
set enable_hashjoin to default;
|
||||||
|
set enable_mergejoin to default;
|
||||||
|
SET enable_seqscan TO DEFAULT;
|
||||||
|
DROP TABLE numrange_test;
|
||||||
|
DROP TABLE numrange_test2;
|
||||||
|
-- test canonical form for int4range
|
||||||
|
select int4range(1,10,'[]');
|
||||||
|
int4range
|
||||||
|
-----------
|
||||||
|
[1,11)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select int4range(1,10,'[)');
|
||||||
|
int4range
|
||||||
|
-----------
|
||||||
|
[1,10)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select int4range(1,10,'(]');
|
||||||
|
int4range
|
||||||
|
-----------
|
||||||
|
[2,11)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select int4range(1,10,'[]');
|
||||||
|
int4range
|
||||||
|
-----------
|
||||||
|
[1,11)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- test canonical form for daterange
|
||||||
|
select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
|
||||||
|
daterange
|
||||||
|
-------------------------
|
||||||
|
[01-10-2000,01-21-2000)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select daterange('2000-01-10'::date, '2000-01-20'::date,'[)');
|
||||||
|
daterange
|
||||||
|
-------------------------
|
||||||
|
[01-10-2000,01-20-2000)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select daterange('2000-01-10'::date, '2000-01-20'::date,'(]');
|
||||||
|
daterange
|
||||||
|
-------------------------
|
||||||
|
[01-11-2000,01-21-2000)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
|
||||||
|
daterange
|
||||||
|
-------------------------
|
||||||
|
[01-10-2000,01-21-2000)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
create table test_range_gist(ir int4range);
|
||||||
|
create index test_range_gist_idx on test_range_gist using gist (ir);
|
||||||
|
insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
|
||||||
|
insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
|
||||||
|
insert into test_range_gist select int4range(g, g+10000) from generate_series(1,1000) g;
|
||||||
|
insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
|
||||||
|
insert into test_range_gist select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
|
||||||
|
insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
|
||||||
|
insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
|
||||||
|
BEGIN;
|
||||||
|
SET LOCAL enable_seqscan = t;
|
||||||
|
SET LOCAL enable_bitmapscan = f;
|
||||||
|
SET LOCAL enable_indexscan = f;
|
||||||
|
select count(*) from test_range_gist where ir @> 'empty'::int4range;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
6200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir = int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> 10;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
130
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
111
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir && int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
158
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir <@ int4range(10,50);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1062
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir << int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
189
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir >> int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3554
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &< int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1029
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &> int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4794
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir -|- int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
BEGIN;
|
||||||
|
SET LOCAL enable_seqscan = f;
|
||||||
|
SET LOCAL enable_bitmapscan = f;
|
||||||
|
SET LOCAL enable_indexscan = t;
|
||||||
|
select count(*) from test_range_gist where ir @> 'empty'::int4range;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
6200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir = int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> 10;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
130
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
111
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir && int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
158
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir <@ int4range(10,50);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1062
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir << int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
189
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir >> int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3554
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir &< int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1029
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir &> int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4794
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir -|- int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
drop index test_range_gist_idx;
|
||||||
|
create index test_range_gist_idx on test_range_gist using gist (ir);
|
||||||
|
BEGIN;
|
||||||
|
SET LOCAL enable_seqscan = f;
|
||||||
|
SET LOCAL enable_bitmapscan = f;
|
||||||
|
SET LOCAL enable_indexscan = t;
|
||||||
|
select count(*) from test_range_gist where ir @> 'empty'::int4range;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
6200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir = int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
2
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> 10;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
130
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
111
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir && int4range(10,20);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
158
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir <@ int4range(10,50);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1062
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir << int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
189
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir >> int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
3554
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir &< int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
1029
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir &> int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
4794
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir -|- int4range(100,500);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
drop table test_range_gist;
|
||||||
|
--
|
||||||
|
-- Btree_gist is not included by default, so to test exclusion
|
||||||
|
-- constraints with range types, use singleton int ranges for the "="
|
||||||
|
-- portion of the constraint.
|
||||||
|
--
|
||||||
|
create table test_range_excl(
|
||||||
|
room int4range,
|
||||||
|
speaker int4range,
|
||||||
|
during tsrange,
|
||||||
|
exclude using gist (room with =, during with &&),
|
||||||
|
exclude using gist (speaker with =, during with &&)
|
||||||
|
);
|
||||||
|
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_room_during_excl" for table "test_range_excl"
|
||||||
|
NOTICE: CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_speaker_during_excl" for table "test_range_excl"
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(123), int4range(1), '[2010-01-02 10:00, 2010-01-02 11:00)');
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(123), int4range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(123), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
|
||||||
|
ERROR: conflicting key value violates exclusion constraint "test_range_excl_room_during_excl"
|
||||||
|
DETAIL: Key (room, during)=([123,124), ["Sat Jan 02 10:10:00 2010","Sat Jan 02 11:10:00 2010")) conflicts with existing key (room, during)=([123,124), ["Sat Jan 02 10:00:00 2010","Sat Jan 02 11:00:00 2010")).
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(124), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
|
||||||
|
ERROR: conflicting key value violates exclusion constraint "test_range_excl_speaker_during_excl"
|
||||||
|
DETAIL: Key (speaker, during)=([1,2), ["Sat Jan 02 10:10:00 2010","Sat Jan 02 11:10:00 2010")) conflicts with existing key (speaker, during)=([1,2), ["Sat Jan 02 10:00:00 2010","Sat Jan 02 11:00:00 2010")).
|
||||||
|
drop table test_range_excl;
|
||||||
|
-- test bigint ranges
|
||||||
|
select int8range(10000000000::int8, 20000000000::int8,'(]');
|
||||||
|
int8range
|
||||||
|
---------------------------
|
||||||
|
[10000000001,20000000001)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- test tstz ranges
|
||||||
|
set timezone to '-08';
|
||||||
|
select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange;
|
||||||
|
tstzrange
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
["Thu Dec 31 22:00:00 2009 -08","Fri Jan 01 02:00:00 2010 -08")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- should fail
|
||||||
|
select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange;
|
||||||
|
ERROR: range lower bound must be less than or equal to range upper bound
|
||||||
|
LINE 1: select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)':...
|
||||||
|
^
|
||||||
|
set timezone to default;
|
||||||
|
--
|
||||||
|
-- Test user-defined range of floats
|
||||||
|
--
|
||||||
|
--should fail
|
||||||
|
create type float8range as range (subtype=float8, subtype_diff=float4mi);
|
||||||
|
ERROR: function float4mi(double precision, double precision) does not exist
|
||||||
|
--should succeed
|
||||||
|
create type float8range as range (subtype=float8, subtype_diff=float8mi);
|
||||||
|
select '[123.001, 5.e9)'::float8range @> 888.882::float8;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
create table float8range_test(f8r float8range, i int);
|
||||||
|
insert into float8range_test values(float8range(-100.00007, '1.111113e9'));
|
||||||
|
select * from float8range_test;
|
||||||
|
f8r | i
|
||||||
|
-------------------------+---
|
||||||
|
[-100.00007,1111113000) |
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop table float8range_test;
|
||||||
|
drop type float8range;
|
||||||
|
--
|
||||||
|
-- Test range types over domains
|
||||||
|
--
|
||||||
|
create domain mydomain as int4;
|
||||||
|
create type mydomainrange as range(subtype=mydomain);
|
||||||
|
select '[4,50)'::mydomainrange @> 7::mydomain;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop type mydomainrange;
|
||||||
|
drop domain mydomain;
|
||||||
|
--
|
||||||
|
-- Test domains over range types
|
||||||
|
--
|
||||||
|
create domain restrictedrange as int4range check (upper(value) < 10);
|
||||||
|
select '[4,5)'::restrictedrange @> 7;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[4,50)'::restrictedrange @> 7; -- should fail
|
||||||
|
ERROR: value for domain restrictedrange violates check constraint "restrictedrange_check"
|
||||||
|
drop domain restrictedrange;
|
||||||
|
--
|
||||||
|
-- Test multiple range types over the same subtype
|
||||||
|
--
|
||||||
|
create type textrange1 as range(subtype=text, collation="C");
|
||||||
|
create type textrange2 as range(subtype=text, collation="C");
|
||||||
|
select textrange1('a','Z') @> 'b'::text;
|
||||||
|
ERROR: range lower bound must be less than or equal to range upper bound
|
||||||
|
select textrange2('a','z') @> 'b'::text;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop type textrange1;
|
||||||
|
drop type textrange2;
|
||||||
|
--
|
||||||
|
-- Test out polymorphic type system
|
||||||
|
--
|
||||||
|
create function anyarray_anyrange_func(a anyarray, r anyrange)
|
||||||
|
returns anyelement as 'select $1[1] + lower($2);' language sql;
|
||||||
|
select anyarray_anyrange_func(ARRAY[1,2], int4range(10,20));
|
||||||
|
anyarray_anyrange_func
|
||||||
|
------------------------
|
||||||
|
11
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- should fail
|
||||||
|
select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
|
||||||
|
ERROR: function anyarray_anyrange_func(integer[], numrange) does not exist
|
||||||
|
LINE 1: select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
|
||||||
|
^
|
||||||
|
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
||||||
|
drop function anyarray_anyrange_func(anyarray, anyrange);
|
||||||
|
-- should fail
|
||||||
|
create function bogus_func(anyelement)
|
||||||
|
returns anyrange as 'select int4range(1,10)' language sql;
|
||||||
|
ERROR: cannot determine result data type
|
||||||
|
DETAIL: A function returning ANYRANGE must have at least one ANYRANGE argument.
|
||||||
|
-- should fail
|
||||||
|
create function bogus_func(int)
|
||||||
|
returns anyrange as 'select int4range(1,10)' language sql;
|
||||||
|
ERROR: cannot determine result data type
|
||||||
|
DETAIL: A function returning a polymorphic type must have at least one polymorphic argument.
|
||||||
|
create function range_add_bounds(anyrange)
|
||||||
|
returns anyelement as 'select lower($1) + upper($1)' language sql;
|
||||||
|
select range_add_bounds(numrange(1.0001, 123.123));
|
||||||
|
range_add_bounds
|
||||||
|
------------------
|
||||||
|
124.1231
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Arrays of ranges
|
||||||
|
--
|
||||||
|
select ARRAY[numrange(1.1), numrange(12.3,155.5)];
|
||||||
|
array
|
||||||
|
------------------------------
|
||||||
|
{"[1.1,1.1]","[12.3,155.5)"}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Ranges of arrays
|
||||||
|
--
|
||||||
|
create type arrayrange as range (subtype=int4[]);
|
||||||
|
select arrayrange(ARRAY[1,2], ARRAY[2,1]);
|
||||||
|
arrayrange
|
||||||
|
-------------------
|
||||||
|
["{1,2}","{2,1}")
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
drop type arrayrange;
|
||||||
|
--
|
||||||
|
-- OUT/INOUT/TABLE functions
|
||||||
|
--
|
||||||
|
create function outparam_succeed(i anyrange, out r anyrange, out t text)
|
||||||
|
as $$ select $1, 'foo' $$ language sql;
|
||||||
|
create function inoutparam_succeed(out i anyelement, inout r anyrange)
|
||||||
|
as $$ select $1, $2 $$ language sql;
|
||||||
|
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
|
||||||
|
as $$ select $1, $2 $$ language sql;
|
||||||
|
-- should fail
|
||||||
|
create function outparam_fail(i anyelement, out r anyrange, out t text)
|
||||||
|
as $$ select '[1,10]', 'foo' $$ language sql;
|
||||||
|
ERROR: cannot determine result data type
|
||||||
|
DETAIL: A function returning ANYRANGE must have at least one ANYRANGE argument.
|
||||||
|
--should fail
|
||||||
|
create function inoutparam_fail(inout i anyelement, out r anyrange)
|
||||||
|
as $$ select $1, '[1,10]' $$ language sql;
|
||||||
|
ERROR: cannot determine result data type
|
||||||
|
DETAIL: A function returning ANYRANGE must have at least one ANYRANGE argument.
|
||||||
|
--should fail
|
||||||
|
create function table_succeed(i anyelement) returns table(i anyelement, r anyrange)
|
||||||
|
as $$ select $1, '[1,10]' $$ language sql;
|
||||||
|
ERROR: cannot determine result data type
|
||||||
|
DETAIL: A function returning ANYRANGE must have at least one ANYRANGE argument.
|
@ -116,6 +116,7 @@ SELECT relname, relhasindex
|
|||||||
pg_opfamily | t
|
pg_opfamily | t
|
||||||
pg_pltemplate | t
|
pg_pltemplate | t
|
||||||
pg_proc | t
|
pg_proc | t
|
||||||
|
pg_range | t
|
||||||
pg_rewrite | t
|
pg_rewrite | t
|
||||||
pg_seclabel | t
|
pg_seclabel | t
|
||||||
pg_shdepend | t
|
pg_shdepend | t
|
||||||
@ -158,7 +159,7 @@ SELECT relname, relhasindex
|
|||||||
timetz_tbl | f
|
timetz_tbl | f
|
||||||
tinterval_tbl | f
|
tinterval_tbl | f
|
||||||
varchar_tbl | f
|
varchar_tbl | f
|
||||||
(147 rows)
|
(148 rows)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- another sanity check: every system catalog that has OIDs should have
|
-- another sanity check: every system catalog that has OIDs should have
|
||||||
|
@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
|
|||||||
FROM pg_type as p1
|
FROM pg_type as p1
|
||||||
WHERE p1.typnamespace = 0 OR
|
WHERE p1.typnamespace = 0 OR
|
||||||
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
|
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
|
||||||
(p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR
|
(p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
|
||||||
NOT p1.typisdefined OR
|
NOT p1.typisdefined OR
|
||||||
(p1.typalign not in ('c', 's', 'i', 'd')) OR
|
(p1.typalign not in ('c', 's', 'i', 'd')) OR
|
||||||
(p1.typstorage not in ('p', 'x', 'e', 'm'));
|
(p1.typstorage not in ('p', 'x', 'e', 'm'));
|
||||||
|
@ -13,7 +13,7 @@ test: tablespace
|
|||||||
# ----------
|
# ----------
|
||||||
# The first group of parallel tests
|
# The first group of parallel tests
|
||||||
# ----------
|
# ----------
|
||||||
test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money
|
test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes
|
||||||
|
|
||||||
# Depends on things setup during char, varchar and text
|
# Depends on things setup during char, varchar and text
|
||||||
test: strings
|
test: strings
|
||||||
|
@ -18,6 +18,7 @@ test: txid
|
|||||||
test: uuid
|
test: uuid
|
||||||
test: enum
|
test: enum
|
||||||
test: money
|
test: money
|
||||||
|
test: rangetypes
|
||||||
test: strings
|
test: strings
|
||||||
test: numerology
|
test: numerology
|
||||||
test: point
|
test: point
|
||||||
|
@ -385,3 +385,14 @@ DROP COLLATION test0 CASCADE;
|
|||||||
|
|
||||||
DROP TABLE collate_dep_test1, collate_dep_test4t;
|
DROP TABLE collate_dep_test1, collate_dep_test4t;
|
||||||
DROP TYPE collate_dep_test2;
|
DROP TYPE collate_dep_test2;
|
||||||
|
|
||||||
|
-- test range types and collations
|
||||||
|
|
||||||
|
create type textrange_c as range(subtype=text, collation="C");
|
||||||
|
create type textrange_en_us as range(subtype=text, collation="en_US");
|
||||||
|
|
||||||
|
select textrange_c('A','Z') @> 'b'::text;
|
||||||
|
select textrange_en_us('A','Z') @> 'b'::text;
|
||||||
|
|
||||||
|
drop type textrange_c;
|
||||||
|
drop type textrange_en_us;
|
||||||
|
@ -133,7 +133,9 @@ WHERE p1.oid != p2.oid AND
|
|||||||
p1.prosrc = p2.prosrc AND
|
p1.prosrc = p2.prosrc AND
|
||||||
p1.prolang = 12 AND p2.prolang = 12 AND
|
p1.prolang = 12 AND p2.prolang = 12 AND
|
||||||
NOT p1.proisagg AND NOT p2.proisagg AND
|
NOT p1.proisagg AND NOT p2.proisagg AND
|
||||||
(p1.prorettype < p2.prorettype)
|
(p1.prorettype < p2.prorettype) AND
|
||||||
|
-- range constructor functions are shared by all range types.
|
||||||
|
NOT p1.prosrc LIKE 'range_constructor%'
|
||||||
ORDER BY 1, 2;
|
ORDER BY 1, 2;
|
||||||
|
|
||||||
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
|
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
|
||||||
@ -142,7 +144,9 @@ WHERE p1.oid != p2.oid AND
|
|||||||
p1.prosrc = p2.prosrc AND
|
p1.prosrc = p2.prosrc AND
|
||||||
p1.prolang = 12 AND p2.prolang = 12 AND
|
p1.prolang = 12 AND p2.prolang = 12 AND
|
||||||
NOT p1.proisagg AND NOT p2.proisagg AND
|
NOT p1.proisagg AND NOT p2.proisagg AND
|
||||||
(p1.proargtypes[0] < p2.proargtypes[0])
|
(p1.proargtypes[0] < p2.proargtypes[0]) AND
|
||||||
|
-- range constructor functions are shared by all range types.
|
||||||
|
NOT p1.prosrc LIKE 'range_constructor%'
|
||||||
ORDER BY 1, 2;
|
ORDER BY 1, 2;
|
||||||
|
|
||||||
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
|
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
|
||||||
@ -151,7 +155,9 @@ WHERE p1.oid != p2.oid AND
|
|||||||
p1.prosrc = p2.prosrc AND
|
p1.prosrc = p2.prosrc AND
|
||||||
p1.prolang = 12 AND p2.prolang = 12 AND
|
p1.prolang = 12 AND p2.prolang = 12 AND
|
||||||
NOT p1.proisagg AND NOT p2.proisagg AND
|
NOT p1.proisagg AND NOT p2.proisagg AND
|
||||||
(p1.proargtypes[1] < p2.proargtypes[1])
|
(p1.proargtypes[1] < p2.proargtypes[1]) AND
|
||||||
|
-- range constructor functions are shared by all range types.
|
||||||
|
NOT p1.prosrc LIKE 'range_constructor%'
|
||||||
ORDER BY 1, 2;
|
ORDER BY 1, 2;
|
||||||
|
|
||||||
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
|
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
|
||||||
|
@ -3600,3 +3600,13 @@ select testoa(1,2,1); -- fail at update
|
|||||||
|
|
||||||
drop function arrayassign1();
|
drop function arrayassign1();
|
||||||
drop function testoa(x1 int, x2 int, x3 int);
|
drop function testoa(x1 int, x2 int, x3 int);
|
||||||
|
|
||||||
|
-- Test resolve_polymorphic_argtypes() codepath. It is only taken when
|
||||||
|
-- a function is invoked from a different backend from where it's defined,
|
||||||
|
-- so we create the a function with polymorphic argument, reconnect, and
|
||||||
|
-- and then call it.
|
||||||
|
create function rangetypes_plpgsql(out a anyelement, b anyrange, c anyarray)
|
||||||
|
language plpgsql as
|
||||||
|
$$ begin a := upper(b) + c[1]; return; end; $$;
|
||||||
|
\c -
|
||||||
|
select rangetypes_plpgsql(int4range(1,10),ARRAY[2,20]);
|
||||||
|
371
src/test/regress/sql/rangetypes.sql
Normal file
371
src/test/regress/sql/rangetypes.sql
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
|
||||||
|
--
|
||||||
|
-- test parser
|
||||||
|
--
|
||||||
|
|
||||||
|
create type textrange as range (subtype=text, collation="C");
|
||||||
|
|
||||||
|
-- negative tests; should fail
|
||||||
|
select ''::textrange;
|
||||||
|
select '-[a,z)'::textrange;
|
||||||
|
select '[a,z) - '::textrange;
|
||||||
|
select '(",a)'::textrange;
|
||||||
|
select '(,,a)'::textrange;
|
||||||
|
select '(),a)'::textrange;
|
||||||
|
select '(a,))'::textrange;
|
||||||
|
select '(],a)'::textrange;
|
||||||
|
select '(a,])'::textrange;
|
||||||
|
|
||||||
|
-- should succeed
|
||||||
|
select ' empty '::textrange;
|
||||||
|
select ' ( empty, empty ) '::textrange;
|
||||||
|
select ' ( " a " " a ", " z " " z " ) '::textrange;
|
||||||
|
select '(,z)'::textrange;
|
||||||
|
select '(a,)'::textrange;
|
||||||
|
select '[,z]'::textrange;
|
||||||
|
select '[a,]'::textrange;
|
||||||
|
select '( , )'::textrange;
|
||||||
|
select '("","")'::textrange;
|
||||||
|
select '["",""]'::textrange;
|
||||||
|
select '(",",",")'::textrange;
|
||||||
|
select '("\\","\\")'::textrange
|
||||||
|
select '(\\,a)'::textrange;
|
||||||
|
select '((,z)'::textrange;
|
||||||
|
select '([,z)'::textrange;
|
||||||
|
select '(!,()'::textrange;
|
||||||
|
select '(!,[)'::textrange;
|
||||||
|
|
||||||
|
drop type textrange;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- create some test data and test the operators
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE numrange_test (nr NUMRANGE);
|
||||||
|
create index numrange_test_btree on numrange_test(nr);
|
||||||
|
SET enable_seqscan = f;
|
||||||
|
|
||||||
|
INSERT INTO numrange_test VALUES('[,)');
|
||||||
|
INSERT INTO numrange_test VALUES('[3,]');
|
||||||
|
INSERT INTO numrange_test VALUES('[, 5)');
|
||||||
|
INSERT INTO numrange_test VALUES(numrange(1.1, 2.2));
|
||||||
|
INSERT INTO numrange_test VALUES('empty');
|
||||||
|
INSERT INTO numrange_test VALUES(numrange(1.7));
|
||||||
|
|
||||||
|
SELECT isempty(nr) FROM numrange_test;
|
||||||
|
SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test
|
||||||
|
WHERE NOT isempty(nr) AND NOT lower_inf(nr) AND NOT upper_inf(nr);
|
||||||
|
|
||||||
|
SELECT * FROM numrange_test WHERE contains(nr, numrange(1.9,1.91));
|
||||||
|
SELECT * FROM numrange_test WHERE nr @> numrange(1.0,10000.1);
|
||||||
|
SELECT * FROM numrange_test WHERE contained_by(numrange(-1e7,-10000.1), nr);
|
||||||
|
SELECT * FROM numrange_test WHERE 1.9 <@ nr;
|
||||||
|
SELECT * FROM numrange_test WHERE nr = 'empty';
|
||||||
|
SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)');
|
||||||
|
SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)';
|
||||||
|
|
||||||
|
select numrange(2.0, 1.0);
|
||||||
|
|
||||||
|
select numrange(2.0, 3.0) -|- numrange(3.0, 4.0);
|
||||||
|
select adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0));
|
||||||
|
select numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()');
|
||||||
|
select numrange(1.0, 2.0) -|- numrange(2.0, 3.0,'[]');
|
||||||
|
select adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]'));
|
||||||
|
|
||||||
|
select numrange(1.1, 3.3) <@ numrange(0.1,10.1);
|
||||||
|
select numrange(0.1, 10.1) <@ numrange(1.1,3.3);
|
||||||
|
|
||||||
|
select numrange(1.1, 2.2) - numrange(2.0, 3.0);
|
||||||
|
select numrange(1.1, 2.2) - numrange(2.2, 3.0);
|
||||||
|
select numrange(1.1, 2.2,'[]') - numrange(2.0, 3.0);
|
||||||
|
select minus(numrange(10.1,12.2,'[]'), numrange(110.0,120.2,'(]'));
|
||||||
|
select minus(numrange(10.1,12.2,'[]'), numrange(0.0,120.2,'(]'));
|
||||||
|
|
||||||
|
select numrange(4.5, 5.5, '[]') && numrange(5.5, 6.5);
|
||||||
|
select numrange(1.0, 2.0) << numrange(3.0, 4.0);
|
||||||
|
select numrange(1.0, 2.0) >> numrange(3.0, 4.0);
|
||||||
|
select numrange(3.0, 70.0) &< numrange(6.6, 100.0);
|
||||||
|
|
||||||
|
select numrange(1.1, 2.2) < numrange(1.0, 200.2);
|
||||||
|
select numrange(1.1, 2.2) < numrange(1.1, 1.2);
|
||||||
|
|
||||||
|
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 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 * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
|
||||||
|
select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
|
||||||
|
select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
|
||||||
|
select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
|
||||||
|
select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
|
||||||
|
select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
|
||||||
|
|
||||||
|
create table numrange_test2(nr numrange);
|
||||||
|
create index numrange_test2_hash_idx on numrange_test2 (nr);
|
||||||
|
INSERT INTO numrange_test2 VALUES('[, 5)');
|
||||||
|
INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
|
||||||
|
INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
|
||||||
|
INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2,'()'));
|
||||||
|
INSERT INTO numrange_test2 VALUES('empty');
|
||||||
|
|
||||||
|
select * from numrange_test2 where nr = 'empty'::numrange;
|
||||||
|
select * from numrange_test2 where nr = numrange(1.1, 2.2);
|
||||||
|
select * from numrange_test2 where nr = numrange(1.1, 2.3);
|
||||||
|
|
||||||
|
set enable_nestloop=t;
|
||||||
|
set enable_hashjoin=f;
|
||||||
|
set enable_mergejoin=f;
|
||||||
|
select * from numrange_test natural join numrange_test2 order by nr;
|
||||||
|
set enable_nestloop=f;
|
||||||
|
set enable_hashjoin=t;
|
||||||
|
set enable_mergejoin=f;
|
||||||
|
select * from numrange_test natural join numrange_test2 order by nr;
|
||||||
|
set enable_nestloop=f;
|
||||||
|
set enable_hashjoin=f;
|
||||||
|
set enable_mergejoin=t;
|
||||||
|
select * from numrange_test natural join numrange_test2 order by nr;
|
||||||
|
|
||||||
|
set enable_nestloop to default;
|
||||||
|
set enable_hashjoin to default;
|
||||||
|
set enable_mergejoin to default;
|
||||||
|
SET enable_seqscan TO DEFAULT;
|
||||||
|
DROP TABLE numrange_test;
|
||||||
|
DROP TABLE numrange_test2;
|
||||||
|
|
||||||
|
-- test canonical form for int4range
|
||||||
|
select int4range(1,10,'[]');
|
||||||
|
select int4range(1,10,'[)');
|
||||||
|
select int4range(1,10,'(]');
|
||||||
|
select int4range(1,10,'[]');
|
||||||
|
|
||||||
|
-- test canonical form for daterange
|
||||||
|
select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
|
||||||
|
select daterange('2000-01-10'::date, '2000-01-20'::date,'[)');
|
||||||
|
select daterange('2000-01-10'::date, '2000-01-20'::date,'(]');
|
||||||
|
select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
|
||||||
|
|
||||||
|
create table test_range_gist(ir int4range);
|
||||||
|
create index test_range_gist_idx on test_range_gist using gist (ir);
|
||||||
|
|
||||||
|
insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
|
||||||
|
insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
|
||||||
|
insert into test_range_gist select int4range(g, g+10000) from generate_series(1,1000) g;
|
||||||
|
insert into test_range_gist select 'empty'::int4range from generate_series(1,500) g;
|
||||||
|
insert into test_range_gist select int4range(NULL,g*10,'(]') from generate_series(1,100) g;
|
||||||
|
insert into test_range_gist select int4range(g*10,NULL,'(]') from generate_series(1,100) g;
|
||||||
|
insert into test_range_gist select int4range(g, g+10) from generate_series(1,2000) g;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
SET LOCAL enable_seqscan = t;
|
||||||
|
SET LOCAL enable_bitmapscan = f;
|
||||||
|
SET LOCAL enable_indexscan = f;
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> 'empty'::int4range;
|
||||||
|
select count(*) from test_range_gist where ir = int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir @> 10;
|
||||||
|
select count(*) from test_range_gist where ir @> int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir && int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir <@ int4range(10,50);
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir << int4range(100,500);
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir >> int4range(100,500);
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &< int4range(100,500);
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir &> int4range(100,500);
|
||||||
|
select count(*) from (select * from test_range_gist where not isempty(ir)) s where ir -|- int4range(100,500);
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
SET LOCAL enable_seqscan = f;
|
||||||
|
SET LOCAL enable_bitmapscan = f;
|
||||||
|
SET LOCAL enable_indexscan = t;
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> 'empty'::int4range;
|
||||||
|
select count(*) from test_range_gist where ir = int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir @> 10;
|
||||||
|
select count(*) from test_range_gist where ir @> int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir && int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir <@ int4range(10,50);
|
||||||
|
select count(*) from test_range_gist where ir << int4range(100,500);
|
||||||
|
select count(*) from test_range_gist where ir >> int4range(100,500);
|
||||||
|
select count(*) from test_range_gist where ir &< int4range(100,500);
|
||||||
|
select count(*) from test_range_gist where ir &> int4range(100,500);
|
||||||
|
select count(*) from test_range_gist where ir -|- int4range(100,500);
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
drop index test_range_gist_idx;
|
||||||
|
create index test_range_gist_idx on test_range_gist using gist (ir);
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
SET LOCAL enable_seqscan = f;
|
||||||
|
SET LOCAL enable_bitmapscan = f;
|
||||||
|
SET LOCAL enable_indexscan = t;
|
||||||
|
|
||||||
|
select count(*) from test_range_gist where ir @> 'empty'::int4range;
|
||||||
|
select count(*) from test_range_gist where ir = int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir @> 10;
|
||||||
|
select count(*) from test_range_gist where ir @> int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir && int4range(10,20);
|
||||||
|
select count(*) from test_range_gist where ir <@ int4range(10,50);
|
||||||
|
select count(*) from test_range_gist where ir << int4range(100,500);
|
||||||
|
select count(*) from test_range_gist where ir >> int4range(100,500);
|
||||||
|
select count(*) from test_range_gist where ir &< int4range(100,500);
|
||||||
|
select count(*) from test_range_gist where ir &> int4range(100,500);
|
||||||
|
select count(*) from test_range_gist where ir -|- int4range(100,500);
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
drop table test_range_gist;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Btree_gist is not included by default, so to test exclusion
|
||||||
|
-- constraints with range types, use singleton int ranges for the "="
|
||||||
|
-- portion of the constraint.
|
||||||
|
--
|
||||||
|
|
||||||
|
create table test_range_excl(
|
||||||
|
room int4range,
|
||||||
|
speaker int4range,
|
||||||
|
during tsrange,
|
||||||
|
exclude using gist (room with =, during with &&),
|
||||||
|
exclude using gist (speaker with =, during with &&)
|
||||||
|
);
|
||||||
|
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(123), int4range(1), '[2010-01-02 10:00, 2010-01-02 11:00)');
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(123), int4range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(123), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(124), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
|
||||||
|
insert into test_range_excl
|
||||||
|
values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
|
||||||
|
|
||||||
|
drop table test_range_excl;
|
||||||
|
|
||||||
|
-- test bigint ranges
|
||||||
|
select int8range(10000000000::int8, 20000000000::int8,'(]');
|
||||||
|
-- test tstz ranges
|
||||||
|
set timezone to '-08';
|
||||||
|
select '[2010-01-01 01:00:00 -05, 2010-01-01 02:00:00 -08)'::tstzrange;
|
||||||
|
-- should fail
|
||||||
|
select '[2010-01-01 01:00:00 -08, 2010-01-01 02:00:00 -05)'::tstzrange;
|
||||||
|
set timezone to default;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test user-defined range of floats
|
||||||
|
--
|
||||||
|
|
||||||
|
--should fail
|
||||||
|
create type float8range as range (subtype=float8, subtype_diff=float4mi);
|
||||||
|
|
||||||
|
--should succeed
|
||||||
|
create type float8range as range (subtype=float8, subtype_diff=float8mi);
|
||||||
|
select '[123.001, 5.e9)'::float8range @> 888.882::float8;
|
||||||
|
create table float8range_test(f8r float8range, i int);
|
||||||
|
insert into float8range_test values(float8range(-100.00007, '1.111113e9'));
|
||||||
|
select * from float8range_test;
|
||||||
|
drop table float8range_test;
|
||||||
|
drop type float8range;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test range types over domains
|
||||||
|
--
|
||||||
|
|
||||||
|
create domain mydomain as int4;
|
||||||
|
create type mydomainrange as range(subtype=mydomain);
|
||||||
|
select '[4,50)'::mydomainrange @> 7::mydomain;
|
||||||
|
drop type mydomainrange;
|
||||||
|
drop domain mydomain;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test domains over range types
|
||||||
|
--
|
||||||
|
|
||||||
|
create domain restrictedrange as int4range check (upper(value) < 10);
|
||||||
|
select '[4,5)'::restrictedrange @> 7;
|
||||||
|
select '[4,50)'::restrictedrange @> 7; -- should fail
|
||||||
|
drop domain restrictedrange;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test multiple range types over the same subtype
|
||||||
|
--
|
||||||
|
|
||||||
|
create type textrange1 as range(subtype=text, collation="C");
|
||||||
|
create type textrange2 as range(subtype=text, collation="C");
|
||||||
|
|
||||||
|
select textrange1('a','Z') @> 'b'::text;
|
||||||
|
select textrange2('a','z') @> 'b'::text;
|
||||||
|
|
||||||
|
drop type textrange1;
|
||||||
|
drop type textrange2;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test out polymorphic type system
|
||||||
|
--
|
||||||
|
|
||||||
|
create function anyarray_anyrange_func(a anyarray, r anyrange)
|
||||||
|
returns anyelement as 'select $1[1] + lower($2);' language sql;
|
||||||
|
|
||||||
|
select anyarray_anyrange_func(ARRAY[1,2], int4range(10,20));
|
||||||
|
|
||||||
|
-- should fail
|
||||||
|
select anyarray_anyrange_func(ARRAY[1,2], numrange(10,20));
|
||||||
|
|
||||||
|
drop function anyarray_anyrange_func(anyarray, anyrange);
|
||||||
|
|
||||||
|
-- should fail
|
||||||
|
create function bogus_func(anyelement)
|
||||||
|
returns anyrange as 'select int4range(1,10)' language sql;
|
||||||
|
|
||||||
|
-- should fail
|
||||||
|
create function bogus_func(int)
|
||||||
|
returns anyrange as 'select int4range(1,10)' language sql;
|
||||||
|
|
||||||
|
create function range_add_bounds(anyrange)
|
||||||
|
returns anyelement as 'select lower($1) + upper($1)' language sql;
|
||||||
|
|
||||||
|
select range_add_bounds(numrange(1.0001, 123.123));
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Arrays of ranges
|
||||||
|
--
|
||||||
|
|
||||||
|
select ARRAY[numrange(1.1), numrange(12.3,155.5)];
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Ranges of arrays
|
||||||
|
--
|
||||||
|
|
||||||
|
create type arrayrange as range (subtype=int4[]);
|
||||||
|
|
||||||
|
select arrayrange(ARRAY[1,2], ARRAY[2,1]);
|
||||||
|
|
||||||
|
drop type arrayrange;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- OUT/INOUT/TABLE functions
|
||||||
|
--
|
||||||
|
|
||||||
|
create function outparam_succeed(i anyrange, out r anyrange, out t text)
|
||||||
|
as $$ select $1, 'foo' $$ language sql;
|
||||||
|
|
||||||
|
create function inoutparam_succeed(out i anyelement, inout r anyrange)
|
||||||
|
as $$ select $1, $2 $$ language sql;
|
||||||
|
|
||||||
|
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
|
||||||
|
as $$ select $1, $2 $$ language sql;
|
||||||
|
|
||||||
|
-- should fail
|
||||||
|
create function outparam_fail(i anyelement, out r anyrange, out t text)
|
||||||
|
as $$ select '[1,10]', 'foo' $$ language sql;
|
||||||
|
|
||||||
|
--should fail
|
||||||
|
create function inoutparam_fail(inout i anyelement, out r anyrange)
|
||||||
|
as $$ select $1, '[1,10]' $$ language sql;
|
||||||
|
|
||||||
|
--should fail
|
||||||
|
create function table_succeed(i anyelement) returns table(i anyelement, r anyrange)
|
||||||
|
as $$ select $1, '[1,10]' $$ language sql;
|
@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
|
|||||||
FROM pg_type as p1
|
FROM pg_type as p1
|
||||||
WHERE p1.typnamespace = 0 OR
|
WHERE p1.typnamespace = 0 OR
|
||||||
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
|
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
|
||||||
(p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR
|
(p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
|
||||||
NOT p1.typisdefined OR
|
NOT p1.typisdefined OR
|
||||||
(p1.typalign not in ('c', 's', 'i', 'd')) OR
|
(p1.typalign not in ('c', 's', 'i', 'd')) OR
|
||||||
(p1.typstorage not in ('p', 'x', 'e', 'm'));
|
(p1.typstorage not in ('p', 'x', 'e', 'm'));
|
||||||
|
Loading…
Reference in New Issue
Block a user