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>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><link linkend="catalog-pg-range"><structname>pg_range</structname></link></entry>
|
||||
<entry>information about range types</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry><link linkend="catalog-pg-rewrite"><structname>pg_rewrite</structname></link></entry>
|
||||
<entry>query rewrite rules</entry>
|
||||
@ -4594,6 +4599,78 @@
|
||||
|
||||
</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">
|
||||
<title><structname>pg_rewrite</structname></title>
|
||||
|
||||
|
@ -4173,6 +4173,8 @@ SET xmloption TO { DOCUMENT | CONTENT };
|
||||
|
||||
&rowtypes;
|
||||
|
||||
&rangetypes;
|
||||
|
||||
<sect1 id="datatype-oid">
|
||||
<title>Object Identifier Types</title>
|
||||
|
||||
@ -4443,6 +4445,10 @@ SELECT * FROM pg_attribute
|
||||
<primary>anyenum</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm zone="datatype-pseudo">
|
||||
<primary>anyrange</primary>
|
||||
</indexterm>
|
||||
|
||||
<indexterm zone="datatype-pseudo">
|
||||
<primary>void</primary>
|
||||
</indexterm>
|
||||
@ -4519,6 +4525,13 @@ SELECT * FROM pg_attribute
|
||||
<xref linkend="datatype-enum">).</entry>
|
||||
</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>
|
||||
<entry><type>anynonarray</></entry>
|
||||
<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
|
||||
<type>trigger</> when the function is used as a trigger). Some also
|
||||
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>
|
||||
|
@ -198,14 +198,15 @@
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
Four pseudo-types of special interest are <type>anyelement</>,
|
||||
<type>anyarray</>, <type>anynonarray</>, and <type>anyenum</>,
|
||||
which are collectively called <firstterm>polymorphic types</>.
|
||||
Any function declared using these types is said to be
|
||||
a <firstterm>polymorphic function</>. A polymorphic function can
|
||||
operate on many different data types, with the specific data type(s)
|
||||
being determined by the data types actually passed to it in a particular
|
||||
call.
|
||||
Five pseudo-types of special interest are <type>anyelement</>,
|
||||
<type>anyarray</>, <type>anynonarray</>, <type>anyenum</>,
|
||||
and <type>anyrange</>, which are collectively
|
||||
called <firstterm>polymorphic types</>. Any function declared
|
||||
using these types is said to be a <firstterm>polymorphic
|
||||
function</>. A polymorphic function can operate on many
|
||||
different data types, with the specific data type(s) being
|
||||
determined by the data types actually passed to it in a
|
||||
particular call.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -221,6 +222,11 @@
|
||||
<type>anyelement</type>, the actual array type in the
|
||||
<type>anyarray</type> positions must be an array whose elements are
|
||||
the same type appearing in the <type>anyelement</type> positions.
|
||||
Similarly, if there are positions declared <type>anyrange</type>
|
||||
and others declared
|
||||
<type>anyelement</type>, the actual range type in the
|
||||
<type>anyrange</type> positions must be a range whose subtype is
|
||||
the same type appearing in the <type>anyelement</type> positions.
|
||||
<type>anynonarray</> is treated exactly the same as <type>anyelement</>,
|
||||
but adds the additional constraint that the actual type must not be
|
||||
an array type.
|
||||
|
@ -25,6 +25,7 @@
|
||||
<!ENTITY mvcc SYSTEM "mvcc.sgml">
|
||||
<!ENTITY perform SYSTEM "perform.sgml">
|
||||
<!ENTITY queries SYSTEM "queries.sgml">
|
||||
<!entity rangetypes SYSTEM "rangetypes.sgml">
|
||||
<!ENTITY rowtypes SYSTEM "rowtypes.sgml">
|
||||
<!ENTITY syntax SYSTEM "syntax.sgml">
|
||||
<!ENTITY textsearch SYSTEM "textsearch.sgml">
|
||||
|
@ -10457,6 +10457,310 @@ SELECT NULLIF(value, '(none)') ...
|
||||
</para>
|
||||
</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">
|
||||
<title>Aggregate Functions</title>
|
||||
|
||||
|
@ -139,7 +139,7 @@
|
||||
<application>PL/pgSQL</> functions can also be declared to accept
|
||||
and return the polymorphic types
|
||||
<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
|
||||
call, as discussed in <xref linkend="extend-types-polymorphic">.
|
||||
An example is shown in <xref linkend="plpgsql-declaration-parameters">.
|
||||
@ -500,8 +500,8 @@ $$ LANGUAGE plpgsql;
|
||||
<para>
|
||||
When the return type of a <application>PL/pgSQL</application>
|
||||
function is declared as a polymorphic type (<type>anyelement</type>,
|
||||
<type>anyarray</type>, <type>anynonarray</type>, or <type>anyenum</>),
|
||||
a special parameter <literal>$0</literal>
|
||||
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
|
||||
or <type>anyrange</type>), a special parameter <literal>$0</literal>
|
||||
is created. Its data type is the actual return type of the function,
|
||||
as deduced from the actual input types (see <xref
|
||||
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
|
||||
( [ '<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> (
|
||||
INPUT = <replaceable class="parameter">input_function</replaceable>,
|
||||
OUTPUT = <replaceable class="parameter">output_function</replaceable>
|
||||
@ -98,11 +107,61 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
|
||||
</para>
|
||||
</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>
|
||||
<title>Base Types</title>
|
||||
|
||||
<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.
|
||||
(This restriction is made because an erroneous type definition could
|
||||
confuse or even crash the server.)
|
||||
|
@ -997,8 +997,8 @@ $$ LANGUAGE SQL;
|
||||
<para>
|
||||
<acronym>SQL</acronym> functions can be declared to accept and
|
||||
return the polymorphic types <type>anyelement</type>,
|
||||
<type>anyarray</type>, <type>anynonarray</type>, and
|
||||
<type>anyenum</type>. See <xref
|
||||
<type>anyarray</type>, <type>anynonarray</type>,
|
||||
<type>anyenum</type>, and <type>anyrange</type>. See <xref
|
||||
linkend="extend-types-polymorphic"> for a more detailed
|
||||
explanation of polymorphic functions. Here is a polymorphic
|
||||
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
|
||||
return the polymorphic types
|
||||
<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
|
||||
of polymorphic functions. When function arguments or return types
|
||||
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 \
|
||||
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_operator.o pg_proc.o pg_db_role_setting.o pg_shdepend.o pg_type.o \
|
||||
storage.o toasting.o
|
||||
pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \
|
||||
pg_type.o storage.o toasting.o
|
||||
|
||||
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_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.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 \
|
||||
)
|
||||
|
||||
|
@ -91,8 +91,11 @@ ProcedureCreate(const char *procedureName,
|
||||
int parameterCount;
|
||||
int allParamCount;
|
||||
Oid *allParams;
|
||||
char *modes = NULL;
|
||||
bool genericInParam = false;
|
||||
bool genericOutParam = false;
|
||||
bool anyrangeInParam = false;
|
||||
bool anyrangeOutParam = false;
|
||||
bool internalInParam = false;
|
||||
bool internalOutParam = false;
|
||||
Oid variadicType = InvalidOid;
|
||||
@ -152,6 +155,24 @@ ProcedureCreate(const char *procedureName,
|
||||
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
|
||||
* is polymorphic. Also, do not allow return type INTERNAL unless at
|
||||
@ -161,6 +182,9 @@ ProcedureCreate(const char *procedureName,
|
||||
{
|
||||
switch (parameterTypes->values[i])
|
||||
{
|
||||
case ANYRANGEOID:
|
||||
anyrangeInParam = true;
|
||||
/* FALL THROUGH */
|
||||
case ANYARRAYOID:
|
||||
case ANYELEMENTOID:
|
||||
case ANYNONARRAYOID:
|
||||
@ -177,14 +201,17 @@ ProcedureCreate(const char *procedureName,
|
||||
{
|
||||
for (i = 0; i < allParamCount; i++)
|
||||
{
|
||||
/*
|
||||
* We don't bother to distinguish input and output params here, so
|
||||
* if there is, say, just an input INTERNAL param then we will
|
||||
* still set internalOutParam. This is OK since we don't really
|
||||
* care.
|
||||
*/
|
||||
if (modes == NULL ||
|
||||
(modes[i] != PROARGMODE_OUT &&
|
||||
modes[i] != PROARGMODE_INOUT &&
|
||||
modes[i] != PROARGMODE_TABLE))
|
||||
continue;
|
||||
|
||||
switch (allParams[i])
|
||||
{
|
||||
case ANYRANGEOID:
|
||||
anyrangeOutParam = true;
|
||||
/* FALL THROUGH */
|
||||
case ANYARRAYOID:
|
||||
case ANYELEMENTOID:
|
||||
case ANYNONARRAYOID:
|
||||
@ -205,6 +232,13 @@ ProcedureCreate(const char *procedureName,
|
||||
errmsg("cannot determine result data type"),
|
||||
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
|
||||
|
||||
if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
|
||||
!anyrangeInParam)
|
||||
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)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
@ -225,23 +259,8 @@ ProcedureCreate(const char *procedureName,
|
||||
procedureName,
|
||||
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
|
||||
* 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_depend.h"
|
||||
#include "catalog/pg_enum.h"
|
||||
#include "catalog/pg_language.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_fn.h"
|
||||
#include "commands/defrem.h"
|
||||
@ -63,6 +67,7 @@
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/rangetypes.h"
|
||||
#include "utils/rel.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "utils/tqual.h"
|
||||
@ -87,6 +92,9 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
|
||||
static Oid findTypeTypmodinFunction(List *procname);
|
||||
static Oid findTypeTypmodoutFunction(List *procname);
|
||||
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 List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
|
||||
static void checkDomainOwner(HeapTuple tup);
|
||||
@ -95,6 +103,8 @@ static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
|
||||
Oid baseTypeOid,
|
||||
int typMod, Constraint *constr,
|
||||
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)
|
||||
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);
|
||||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
@ -724,14 +742,15 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
basetypeoid = HeapTupleGetOid(typeTup);
|
||||
|
||||
/*
|
||||
* Base type must be a plain base type, another domain or an enum. Domains
|
||||
* over pseudotypes would create a security hole. Domains over composite
|
||||
* types might be made to work in the future, but not today.
|
||||
* Base type must be a plain base type, another domain, an enum or a range
|
||||
* type. Domains over pseudotypes would create a security hole. Domains
|
||||
* over composite types might be made to work in the future, but not today.
|
||||
*/
|
||||
typtype = baseType->typtype;
|
||||
if (typtype != TYPTYPE_BASE &&
|
||||
typtype != TYPTYPE_DOMAIN &&
|
||||
typtype != TYPTYPE_ENUM)
|
||||
typtype != TYPTYPE_ENUM &&
|
||||
typtype != TYPTYPE_RANGE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("\"%s\" is not a valid base type for a domain",
|
||||
@ -1134,6 +1153,327 @@ DefineEnum(CreateEnumStmt *stmt)
|
||||
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
|
||||
* Adds a new label to an existing enum.
|
||||
@ -1449,6 +1789,103 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
|
||||
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
|
||||
*
|
||||
|
@ -1352,6 +1352,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
||||
if (fn_typtype == TYPTYPE_BASE ||
|
||||
fn_typtype == TYPTYPE_DOMAIN ||
|
||||
fn_typtype == TYPTYPE_ENUM ||
|
||||
fn_typtype == TYPTYPE_RANGE ||
|
||||
rettype == VOIDOID)
|
||||
{
|
||||
/*
|
||||
|
@ -3055,6 +3055,17 @@ _copyCreateEnumStmt(CreateEnumStmt *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static CreateRangeStmt *
|
||||
_copyCreateRangeStmt(CreateRangeStmt *from)
|
||||
{
|
||||
CreateRangeStmt *newnode = makeNode(CreateRangeStmt);
|
||||
|
||||
COPY_NODE_FIELD(typeName);
|
||||
COPY_NODE_FIELD(params);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static AlterEnumStmt *
|
||||
_copyAlterEnumStmt(AlterEnumStmt *from)
|
||||
{
|
||||
@ -4297,6 +4308,9 @@ copyObject(void *from)
|
||||
case T_CreateEnumStmt:
|
||||
retval = _copyCreateEnumStmt(from);
|
||||
break;
|
||||
case T_CreateRangeStmt:
|
||||
retval = _copyCreateRangeStmt(from);
|
||||
break;
|
||||
case T_AlterEnumStmt:
|
||||
retval = _copyAlterEnumStmt(from);
|
||||
break;
|
||||
|
@ -1438,6 +1438,15 @@ _equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalCreateRangeStmt(CreateRangeStmt *a, CreateRangeStmt *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(typeName);
|
||||
COMPARE_NODE_FIELD(params);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalAlterEnumStmt(AlterEnumStmt *a, AlterEnumStmt *b)
|
||||
{
|
||||
@ -2826,6 +2835,9 @@ equal(void *a, void *b)
|
||||
case T_CreateEnumStmt:
|
||||
retval = _equalCreateEnumStmt(a, b);
|
||||
break;
|
||||
case T_CreateRangeStmt:
|
||||
retval = _equalCreateRangeStmt(a, b);
|
||||
break;
|
||||
case T_AlterEnumStmt:
|
||||
retval = _equalAlterEnumStmt(a, b);
|
||||
break;
|
||||
|
@ -4336,6 +4336,13 @@ DefineStmt:
|
||||
n->vals = $7;
|
||||
$$ = (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
|
||||
{
|
||||
DefineStmt *n = makeNode(DefineStmt);
|
||||
|
@ -158,7 +158,8 @@ coerce_type(ParseState *pstate, Node *node,
|
||||
return node;
|
||||
}
|
||||
if (targetTypeId == ANYARRAYOID ||
|
||||
targetTypeId == ANYENUMOID)
|
||||
targetTypeId == ANYENUMOID ||
|
||||
targetTypeId == ANYRANGEOID)
|
||||
{
|
||||
/*
|
||||
* 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,
|
||||
* and must in fact be varlena arrays.
|
||||
* 2) All arguments declared ANYELEMENT must have matching datatypes.
|
||||
* 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
|
||||
* the actual ANYELEMENT datatype is in fact the element type for
|
||||
* the actual ANYARRAY datatype.
|
||||
* 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure the
|
||||
* actual ANYELEMENT datatype is in fact the element type for the actual
|
||||
* 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
|
||||
* (alone or in combination with plain ANYELEMENT), we add the extra
|
||||
* 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 array_typeid = InvalidOid;
|
||||
Oid array_typelem;
|
||||
Oid range_typeid = InvalidOid;
|
||||
Oid range_typelem;
|
||||
bool have_anyelement = false;
|
||||
bool have_anynonarray = false;
|
||||
bool have_anyenum = false;
|
||||
@ -1348,6 +1353,15 @@ check_generic_type_consistency(Oid *actual_arg_types,
|
||||
return false;
|
||||
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 */
|
||||
@ -1393,6 +1407,27 @@ check_generic_type_consistency(Oid *actual_arg_types,
|
||||
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 */
|
||||
return true;
|
||||
}
|
||||
@ -1416,23 +1451,28 @@ check_generic_type_consistency(Oid *actual_arg_types,
|
||||
* if it is declared as a polymorphic type:
|
||||
*
|
||||
* 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
|
||||
* argument's actual type as the function's return type.
|
||||
* 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument
|
||||
* is ANYELEMENT, use the actual type of the argument to determine
|
||||
* the function's return type, i.e. the element type's corresponding
|
||||
* array type.
|
||||
* argument's actual type as the function's return type. Similarly, if
|
||||
* return type is ANYRANGE, and any argument is ANYRANGE, use the argument's
|
||||
* actual type as the function's return type.
|
||||
* 2) If return type is ANYARRAY, no argument is ANYARRAY, but any argument is
|
||||
* 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,
|
||||
* generate an ERROR. This condition is prevented by CREATE FUNCTION
|
||||
* and is therefore not expected here.
|
||||
* generate an ERROR. Similarly, if the return type is ANYRANGE, and no
|
||||
* 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
|
||||
* argument's actual type as the function's return type.
|
||||
* 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any
|
||||
* argument is ANYARRAY, use the actual type of the argument to determine
|
||||
* the function's return type, i.e. the array type's corresponding
|
||||
* element type.
|
||||
* 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
|
||||
* generate an ERROR. This condition is prevented by CREATE FUNCTION
|
||||
* and is therefore not expected here.
|
||||
* 5) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument
|
||||
* 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 element
|
||||
* type or the range type's corresponding subtype (or both, in which case
|
||||
* they must match).
|
||||
* 6) If return type is ANYELEMENT, no argument is ANYARRAY, ANYRANGE, or
|
||||
* 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
|
||||
* (alone or in combination with plain ANYELEMENT), we add the extra
|
||||
* 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
|
||||
* is an extra restriction if not.)
|
||||
*
|
||||
* Domains over arrays match ANYARRAY arguments, and are immediately flattened
|
||||
* to their base type. (In particular, if the return type is also ANYARRAY,
|
||||
* we'll set it to the base type not the domain type.)
|
||||
* Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
|
||||
* respectively, and are immediately flattened to their base type. (In
|
||||
* 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
|
||||
* 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;
|
||||
Oid elem_typeid = InvalidOid;
|
||||
Oid array_typeid = InvalidOid;
|
||||
Oid range_typeid = InvalidOid;
|
||||
Oid array_typelem;
|
||||
Oid range_typelem;
|
||||
bool have_anyelement = (rettype == ANYELEMENTOID ||
|
||||
rettype == ANYNONARRAYOID ||
|
||||
rettype == ANYENUMOID);
|
||||
@ -1534,6 +1577,26 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
||||
format_type_be(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))));
|
||||
}
|
||||
}
|
||||
/* 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))
|
||||
{
|
||||
if (allow_poly)
|
||||
@ -1645,6 +1736,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 (rettype == ANYELEMENTOID ||
|
||||
rettype == ANYNONARRAYOID ||
|
||||
@ -1711,7 +1826,8 @@ resolve_generic_type(Oid declared_type,
|
||||
}
|
||||
else if (context_declared_type == ANYELEMENTOID ||
|
||||
context_declared_type == ANYNONARRAYOID ||
|
||||
context_declared_type == ANYENUMOID)
|
||||
context_declared_type == ANYENUMOID ||
|
||||
context_declared_type == ANYRANGEOID)
|
||||
{
|
||||
/* Use the array type corresponding to 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 ||
|
||||
declared_type == ANYNONARRAYOID ||
|
||||
declared_type == ANYENUMOID)
|
||||
declared_type == ANYENUMOID ||
|
||||
declared_type == ANYRANGEOID)
|
||||
{
|
||||
if (context_declared_type == ANYARRAYOID)
|
||||
{
|
||||
@ -1741,6 +1858,18 @@ resolve_generic_type(Oid declared_type,
|
||||
format_type_be(context_base_type))));
|
||||
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 ||
|
||||
context_declared_type == ANYNONARRAYOID ||
|
||||
context_declared_type == ANYENUMOID)
|
||||
@ -1854,6 +1983,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
|
||||
if (type_is_enum(srctype))
|
||||
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 */
|
||||
if (targettype == RECORDOID)
|
||||
if (ISCOMPLEX(srctype))
|
||||
|
@ -202,6 +202,7 @@ check_xact_readonly(Node *parsetree)
|
||||
case T_CreateTrigStmt:
|
||||
case T_CompositeTypeStmt:
|
||||
case T_CreateEnumStmt:
|
||||
case T_CreateRangeStmt:
|
||||
case T_AlterEnumStmt:
|
||||
case T_ViewStmt:
|
||||
case T_DropCastStmt:
|
||||
@ -870,6 +871,10 @@ standard_ProcessUtility(Node *parsetree,
|
||||
DefineEnum((CreateEnumStmt *) parsetree);
|
||||
break;
|
||||
|
||||
case T_CreateRangeStmt:
|
||||
DefineRange((CreateRangeStmt *) parsetree);
|
||||
break;
|
||||
|
||||
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
|
||||
|
||||
/*
|
||||
@ -1854,6 +1859,10 @@ CreateCommandTag(Node *parsetree)
|
||||
tag = "CREATE TYPE";
|
||||
break;
|
||||
|
||||
case T_CreateRangeStmt:
|
||||
tag = "CREATE TYPE";
|
||||
break;
|
||||
|
||||
case T_AlterEnumStmt:
|
||||
tag = "ALTER TYPE";
|
||||
break;
|
||||
@ -2401,6 +2410,10 @@ GetCommandLogLevel(Node *parsetree)
|
||||
lev = LOGSTMT_DDL;
|
||||
break;
|
||||
|
||||
case T_CreateRangeStmt:
|
||||
lev = LOGSTMT_DDL;
|
||||
break;
|
||||
|
||||
case T_AlterEnumStmt:
|
||||
lev = LOGSTMT_DDL;
|
||||
break;
|
||||
|
@ -20,8 +20,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
|
||||
enum.o float.o format_type.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 \
|
||||
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
|
||||
regexp.o regproc.o ruleutils.o selfuncs.o \
|
||||
oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.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 \
|
||||
network.o mac.o inet_cidr_ntop.o inet_net_pton.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);
|
||||
}
|
||||
|
||||
|
||||
/* timestamp_date()
|
||||
* Convert timestamp to date data type.
|
||||
*/
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "libpq/pqformat.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/rangetypes.h"
|
||||
|
||||
|
||||
/*
|
||||
@ -187,6 +188,29 @@ anyenum_out(PG_FUNCTION_ARGS)
|
||||
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.
|
||||
|
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_operator.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_range.h"
|
||||
#include "catalog/pg_statistic.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "miscadmin.h"
|
||||
@ -2250,6 +2251,16 @@ type_is_enum(Oid typid)
|
||||
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
|
||||
*
|
||||
@ -2855,3 +2866,22 @@ get_namespace_name(Oid nspid)
|
||||
else
|
||||
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_opfamily.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_range.h"
|
||||
#include "catalog/pg_rewrite.h"
|
||||
#include "catalog/pg_statistic.h"
|
||||
#include "catalog/pg_tablespace.h"
|
||||
@ -554,6 +555,17 @@ static const struct cachedesc cacheinfo[] = {
|
||||
},
|
||||
2048
|
||||
},
|
||||
{RangeRelationId, /* RANGETYPE */
|
||||
RangeTypidIndexId,
|
||||
1,
|
||||
{
|
||||
Anum_pg_range_rngtypid,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
1024
|
||||
},
|
||||
{RelationRelationId, /* RELNAMENSP */
|
||||
ClassNameNspIndexId,
|
||||
2,
|
||||
|
@ -407,11 +407,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
|
||||
int nargs = declared_args->dim1;
|
||||
bool have_anyelement_result = false;
|
||||
bool have_anyarray_result = false;
|
||||
bool have_anyrange_result = false;
|
||||
bool have_anynonarray = false;
|
||||
bool have_anyenum = false;
|
||||
Oid anyelement_type = InvalidOid;
|
||||
Oid anyarray_type = InvalidOid;
|
||||
Oid anycollation;
|
||||
Oid anyrange_type = InvalidOid;
|
||||
Oid anycollation = InvalidOid;
|
||||
int i;
|
||||
|
||||
/* 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_anyenum = true;
|
||||
break;
|
||||
case ANYRANGEOID:
|
||||
have_anyrange_result = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!have_anyelement_result && !have_anyarray_result)
|
||||
if (!have_anyelement_result && !have_anyarray_result &&
|
||||
!have_anyrange_result)
|
||||
return true;
|
||||
|
||||
/*
|
||||
@ -461,20 +467,47 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
|
||||
if (!OidIsValid(anyarray_type))
|
||||
anyarray_type = get_call_expr_argtype(call_expr, i);
|
||||
break;
|
||||
case ANYRANGEOID:
|
||||
if (!OidIsValid(anyrange_type))
|
||||
anyrange_type = get_call_expr_argtype(call_expr, i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* If needed, deduce one polymorphic type from the other */
|
||||
if (have_anyelement_result && !OidIsValid(anyelement_type))
|
||||
anyelement_type = resolve_generic_type(ANYELEMENTOID,
|
||||
anyarray_type,
|
||||
ANYARRAYOID);
|
||||
{
|
||||
if (OidIsValid(anyarray_type))
|
||||
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))
|
||||
anyarray_type = resolve_generic_type(ANYARRAYOID,
|
||||
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
|
||||
* 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))
|
||||
{
|
||||
/*
|
||||
@ -529,6 +567,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
|
||||
0);
|
||||
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
|
||||
break;
|
||||
case ANYRANGEOID:
|
||||
TupleDescInitEntry(tupdesc, i + 1,
|
||||
NameStr(tupdesc->attrs[i]->attname),
|
||||
anyrange_type,
|
||||
-1,
|
||||
0);
|
||||
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -552,8 +598,10 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
||||
{
|
||||
bool have_anyelement_result = false;
|
||||
bool have_anyarray_result = false;
|
||||
bool have_anyrange_result = false;
|
||||
Oid anyelement_type = InvalidOid;
|
||||
Oid anyarray_type = InvalidOid;
|
||||
Oid anyrange_type = InvalidOid;
|
||||
int inargno;
|
||||
int i;
|
||||
|
||||
@ -597,6 +645,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
||||
argtypes[i] = anyarray_type;
|
||||
}
|
||||
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:
|
||||
break;
|
||||
}
|
||||
@ -605,18 +668,42 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
||||
}
|
||||
|
||||
/* Done? */
|
||||
if (!have_anyelement_result && !have_anyarray_result)
|
||||
if (!have_anyelement_result && !have_anyarray_result &&
|
||||
!have_anyrange_result)
|
||||
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 (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
|
||||
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
|
||||
!OidIsValid(anyrange_type))
|
||||
return false;
|
||||
|
||||
/* If needed, deduce one polymorphic type from the other */
|
||||
if (have_anyelement_result && !OidIsValid(anyelement_type))
|
||||
anyelement_type = resolve_generic_type(ANYELEMENTOID,
|
||||
anyarray_type,
|
||||
ANYARRAYOID);
|
||||
{
|
||||
if (OidIsValid(anyarray_type))
|
||||
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))
|
||||
anyarray_type = resolve_generic_type(ANYARRAYOID,
|
||||
anyelement_type,
|
||||
@ -637,6 +724,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
|
||||
case ANYARRAYOID:
|
||||
argtypes[i] = anyarray_type;
|
||||
break;
|
||||
case ANYRANGEOID:
|
||||
argtypes[i] = anyrange_type;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -663,6 +753,7 @@ get_type_func_class(Oid typid)
|
||||
case TYPTYPE_BASE:
|
||||
case TYPTYPE_DOMAIN:
|
||||
case TYPTYPE_ENUM:
|
||||
case TYPTYPE_RANGE:
|
||||
return TYPEFUNC_SCALAR;
|
||||
case TYPTYPE_PSEUDO:
|
||||
if (typid == RECORDOID)
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "catalog/pg_largeobject.h"
|
||||
#include "catalog/pg_largeobject_metadata.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_range.h"
|
||||
#include "catalog/pg_trigger.h"
|
||||
#include "catalog/pg_type.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 dumpBaseType(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 dumpCompositeType(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
|
||||
* 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->dobj.objType = DO_SHELL_TYPE;
|
||||
@ -3700,7 +3703,32 @@ getFuncs(int *numFuncs)
|
||||
* 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,
|
||||
"SELECT tableoid, oid, proname, prolang, "
|
||||
@ -7309,6 +7337,8 @@ dumpType(Archive *fout, TypeInfo *tyinfo)
|
||||
dumpCompositeType(fout, tyinfo);
|
||||
else if (tyinfo->typtype == TYPTYPE_ENUM)
|
||||
dumpEnumType(fout, tyinfo);
|
||||
else if (tyinfo->typtype == TYPTYPE_RANGE)
|
||||
dumpRangeType(fout, tyinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -7432,6 +7462,156 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
|
||||
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
|
||||
* writes out to fout the queries to recreate a user-defined base type
|
||||
|
@ -53,6 +53,7 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 201110221
|
||||
/* COMMITTER: please set appropriately */
|
||||
#define CATALOG_VERSION_NO 201111111
|
||||
|
||||
#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));
|
||||
#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 */
|
||||
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 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 */
|
||||
|
@ -336,5 +336,14 @@ DATA(insert ( 3659 3614 3614 4 3658 ));
|
||||
DATA(insert ( 3659 3614 3614 5 2700 ));
|
||||
DATA(insert ( 3626 3614 3614 1 3622 ));
|
||||
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 */
|
||||
|
@ -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 ( 403 tsquery_ops PGNSP PGUID 3683 3615 t 0 ));
|
||||
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 */
|
||||
|
@ -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 ));
|
||||
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
|
||||
|
@ -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 = 3683 ( 403 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 */
|
||||
|
@ -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_ ));
|
||||
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
|
||||
|
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");
|
||||
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
|
||||
*
|
||||
@ -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
|
||||
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
|
||||
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_ENUM 'e' /* enumerated type */
|
||||
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
|
||||
#define TYPTYPE_RANGE 'r' /* range type */
|
||||
|
||||
#define TYPCATEGORY_INVALID '\0' /* not an allowed category */
|
||||
#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_NUMERIC 'N'
|
||||
#define TYPCATEGORY_PSEUDOTYPE 'P'
|
||||
#define TYPCATEGORY_RANGE 'R'
|
||||
#define TYPCATEGORY_STRING 'S'
|
||||
#define TYPCATEGORY_TIMESPAN 'T'
|
||||
#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) == ANYARRAYOID || \
|
||||
(typid) == ANYNONARRAYOID || \
|
||||
(typid) == ANYENUMOID)
|
||||
(typid) == ANYENUMOID || \
|
||||
(typid) == ANYRANGEOID)
|
||||
|
||||
#endif /* PG_TYPE_H */
|
||||
|
@ -23,6 +23,7 @@ extern void DefineType(List *names, List *parameters);
|
||||
extern void RemoveTypeById(Oid typeOid);
|
||||
extern void DefineDomain(CreateDomainStmt *stmt);
|
||||
extern void DefineEnum(CreateEnumStmt *stmt);
|
||||
extern void DefineRange(CreateRangeStmt *stmt);
|
||||
extern void AlterEnum(AlterEnumStmt *stmt);
|
||||
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
|
||||
extern Oid AssignTypeArrayOid(void);
|
||||
|
@ -346,6 +346,7 @@ typedef enum NodeTag
|
||||
T_ReassignOwnedStmt,
|
||||
T_CompositeTypeStmt,
|
||||
T_CreateEnumStmt,
|
||||
T_CreateRangeStmt,
|
||||
T_AlterEnumStmt,
|
||||
T_AlterTSDictionaryStmt,
|
||||
T_AlterTSConfigurationStmt,
|
||||
|
@ -2334,6 +2334,17 @@ typedef struct AlterEnumStmt
|
||||
bool newValIsAfter; /* place new enum value after neighbor? */
|
||||
} 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
|
||||
* ----------------------
|
||||
|
@ -119,6 +119,7 @@ extern Node *get_typdefault(Oid typid);
|
||||
extern char get_typtype(Oid typid);
|
||||
extern bool type_is_rowtype(Oid typid);
|
||||
extern bool type_is_enum(Oid typid);
|
||||
extern bool type_is_range(Oid typid);
|
||||
extern void get_type_category_preferred(Oid typid,
|
||||
char *typcategory,
|
||||
bool *typispreferred);
|
||||
@ -147,6 +148,7 @@ extern void free_attstatsslot(Oid atttype,
|
||||
Datum *values, int nvalues,
|
||||
float4 *numbers, int nnumbers);
|
||||
extern char *get_namespace_name(Oid nspid);
|
||||
extern Oid get_range_subtype(Oid rangeOid);
|
||||
|
||||
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
|
||||
/* 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,
|
||||
PROCNAMEARGSNSP,
|
||||
PROCOID,
|
||||
RANGETYPE,
|
||||
RELNAMENSP,
|
||||
RELOID,
|
||||
RULERELNAME,
|
||||
|
@ -490,6 +490,8 @@ do_compile(FunctionCallInfo fcinfo,
|
||||
{
|
||||
if (rettypeid == ANYARRAYOID)
|
||||
rettypeid = INT4ARRAYOID;
|
||||
else if (rettypeid == ANYRANGEOID)
|
||||
rettypeid = INT4RANGEOID;
|
||||
else /* ANYELEMENT or ANYNONARRAY */
|
||||
rettypeid = INT4OID;
|
||||
/* XXX what could we use for ANYENUM? */
|
||||
@ -2119,6 +2121,7 @@ build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
|
||||
case TYPTYPE_BASE:
|
||||
case TYPTYPE_DOMAIN:
|
||||
case TYPTYPE_ENUM:
|
||||
case TYPTYPE_RANGE:
|
||||
typ->ttype = PLPGSQL_TTYPE_SCALAR;
|
||||
break;
|
||||
case TYPTYPE_COMPOSITE:
|
||||
@ -2373,8 +2376,8 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
|
||||
/*
|
||||
* This is the same as the standard resolve_polymorphic_argtypes() function,
|
||||
* but with a special case for validation: assume that polymorphic arguments
|
||||
* are integer or integer-array. Also, we go ahead and report the error
|
||||
* if we can't resolve the types.
|
||||
* are integer, integer-range or integer-array. Also, we go ahead and report
|
||||
* the error if we can't resolve the types.
|
||||
*/
|
||||
static void
|
||||
plpgsql_resolve_polymorphic_argtypes(int numargs,
|
||||
@ -2407,6 +2410,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
|
||||
case ANYENUMOID: /* XXX dubious */
|
||||
argtypes[i] = INT4OID;
|
||||
break;
|
||||
case ANYRANGEOID:
|
||||
argtypes[i] = INT4RANGEOID;
|
||||
break;
|
||||
case ANYARRAYOID:
|
||||
argtypes[i] = INT4ARRAYOID;
|
||||
break;
|
||||
|
@ -1049,3 +1049,20 @@ Composite type "public.collate_dep_test2"
|
||||
|
||||
DROP TABLE collate_dep_test1, collate_dep_test4t;
|
||||
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.prolang = 12 AND p2.prolang = 12 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;
|
||||
prorettype | prorettype
|
||||
------------+------------
|
||||
@ -161,7 +163,9 @@ WHERE p1.oid != p2.oid AND
|
||||
p1.prosrc = p2.prosrc AND
|
||||
p1.prolang = 12 AND p2.prolang = 12 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;
|
||||
proargtypes | proargtypes
|
||||
-------------+-------------
|
||||
@ -178,7 +182,9 @@ WHERE p1.oid != p2.oid AND
|
||||
p1.prosrc = p2.prosrc AND
|
||||
p1.prolang = 12 AND p2.prolang = 12 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;
|
||||
proargtypes | proargtypes
|
||||
-------------+-------------
|
||||
@ -1015,19 +1021,30 @@ ORDER BY 1, 2, 3;
|
||||
403 | 5 | ~>~
|
||||
405 | 1 | =
|
||||
783 | 1 | <<
|
||||
783 | 1 | =
|
||||
783 | 1 | @@
|
||||
783 | 2 | &<
|
||||
783 | 2 | <>
|
||||
783 | 3 | &&
|
||||
783 | 4 | &>
|
||||
783 | 4 | @>
|
||||
783 | 5 | <@
|
||||
783 | 5 | >>
|
||||
783 | 6 | @>
|
||||
783 | 6 | ~=
|
||||
783 | 7 | <@
|
||||
783 | 7 | @>
|
||||
783 | 8 | <<
|
||||
783 | 8 | <@
|
||||
783 | 9 | &<|
|
||||
783 | 9 | >>
|
||||
783 | 10 | &<
|
||||
783 | 10 | <<|
|
||||
783 | 10 | <^
|
||||
783 | 11 | &>
|
||||
783 | 11 | >^
|
||||
783 | 11 | |>>
|
||||
783 | 12 | -|-
|
||||
783 | 12 | |&>
|
||||
783 | 13 | ~
|
||||
783 | 14 | @
|
||||
@ -1044,7 +1061,7 @@ ORDER BY 1, 2, 3;
|
||||
2742 | 2 | @@@
|
||||
2742 | 3 | <@
|
||||
2742 | 4 | =
|
||||
(40 rows)
|
||||
(51 rows)
|
||||
|
||||
-- Check that all opclass search operators have selectivity estimators.
|
||||
-- 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
|
||||
WHERE p1.amopopr = p2.oid AND p1.amoppurpose = 's' AND
|
||||
(p2.oprrest = 0 OR p2.oprjoin = 0);
|
||||
amopfamily | amopopr | oid | oprname
|
||||
------------+---------+-----+---------
|
||||
(0 rows)
|
||||
amopfamily | amopopr | oid | oprname
|
||||
------------+---------+------+---------
|
||||
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
|
||||
-- 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
|
||||
drop function arrayassign1();
|
||||
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_pltemplate | t
|
||||
pg_proc | t
|
||||
pg_range | t
|
||||
pg_rewrite | t
|
||||
pg_seclabel | t
|
||||
pg_shdepend | t
|
||||
@ -158,7 +159,7 @@ SELECT relname, relhasindex
|
||||
timetz_tbl | f
|
||||
tinterval_tbl | f
|
||||
varchar_tbl | f
|
||||
(147 rows)
|
||||
(148 rows)
|
||||
|
||||
--
|
||||
-- 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
|
||||
WHERE p1.typnamespace = 0 OR
|
||||
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
|
||||
(p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR
|
||||
(p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
|
||||
NOT p1.typisdefined OR
|
||||
(p1.typalign not in ('c', 's', 'i', 'd')) OR
|
||||
(p1.typstorage not in ('p', 'x', 'e', 'm'));
|
||||
|
@ -13,7 +13,7 @@ test: tablespace
|
||||
# ----------
|
||||
# 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
|
||||
test: strings
|
||||
|
@ -18,6 +18,7 @@ test: txid
|
||||
test: uuid
|
||||
test: enum
|
||||
test: money
|
||||
test: rangetypes
|
||||
test: strings
|
||||
test: numerology
|
||||
test: point
|
||||
|
@ -385,3 +385,14 @@ DROP COLLATION test0 CASCADE;
|
||||
|
||||
DROP TABLE collate_dep_test1, collate_dep_test4t;
|
||||
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.prolang = 12 AND p2.prolang = 12 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;
|
||||
|
||||
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
|
||||
@ -142,7 +144,9 @@ WHERE p1.oid != p2.oid AND
|
||||
p1.prosrc = p2.prosrc AND
|
||||
p1.prolang = 12 AND p2.prolang = 12 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;
|
||||
|
||||
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
|
||||
@ -151,7 +155,9 @@ WHERE p1.oid != p2.oid AND
|
||||
p1.prosrc = p2.prosrc AND
|
||||
p1.prolang = 12 AND p2.prolang = 12 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;
|
||||
|
||||
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 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
|
||||
WHERE p1.typnamespace = 0 OR
|
||||
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
|
||||
(p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR
|
||||
(p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
|
||||
NOT p1.typisdefined OR
|
||||
(p1.typalign not in ('c', 's', 'i', 'd')) OR
|
||||
(p1.typstorage not in ('p', 'x', 'e', 'm'));
|
||||
|
Loading…
Reference in New Issue
Block a user