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:
Heikki Linnakangas 2011-11-03 13:16:28 +02:00
parent 4334289186
commit 4429f6a9e3
58 changed files with 6718 additions and 103 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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>&lt;&gt;</literal> </entry>
<entry>not equal</entry>
<entry><literal>numrange(1.1,2.2) &lt;&gt; numrange(1.1,2.3)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&lt;</literal> </entry>
<entry>less than</entry>
<entry><literal>int4range(1,10) &lt; int4range(2,3)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&gt;</literal> </entry>
<entry>greater than</entry>
<entry><literal>int4range(1,10) &gt; int4range(1,5)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&lt;=</literal> </entry>
<entry>less than or equal</entry>
<entry><literal>numrange(1.1,2.2) &lt;= numrange(1.1,2.2)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&gt;=</literal> </entry>
<entry>greater than or equal</entry>
<entry><literal>numrange(1.1,2.2) &gt;= numrange(1.1,2.0)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>@&gt;</literal> </entry>
<entry>contains</entry>
<entry><literal>'[2011-01-01,2011-03-01)'::tsrange @&gt; '2011-01-10'::timestamp</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&lt;@</literal> </entry>
<entry>is contained by</entry>
<entry><literal>int4range(2,4) &lt;@ int4range(1,7)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&amp;&amp;</literal> </entry>
<entry>overlap (have points in common)</entry>
<entry><literal>int8range(3,7) &amp;&amp; int8range(4,12)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&lt;&lt;</literal> </entry>
<entry>strictly left of</entry>
<entry><literal>int8range(1,10) &lt;&lt; int8range(100,110)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&gt;&gt;</literal> </entry>
<entry>strictly right of</entry>
<entry><literal>int8range(50,60) &gt;&gt; int8range(20,30)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&amp;&lt;</literal> </entry>
<entry>Does not extend to the right of?</entry>
<entry><literal>int8range(1,20) &amp;&lt; int8range(18,20)</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>&amp;&gt;</literal> </entry>
<entry>Does not extend to the left of?</entry>
<entry><literal>int8range(7,20) &amp;&gt; 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>

View File

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

View 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>&amp;&amp;</literal>
(overlaps), <literal>@&gt;</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>

View File

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

View File

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

View File

@ -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 \
)

View File

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

View 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);
}

View File

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

View File

@ -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)
{
/*

View File

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

View File

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

View File

@ -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);

View File

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

View File

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

View File

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

View File

@ -889,7 +889,6 @@ date_timestamp(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMP(result);
}
/* timestamp_date()
* Convert timestamp to date data type.
*/

View File

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

File diff suppressed because it is too large Load Diff

View 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);
}

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

@ -53,6 +53,7 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201110221
/* COMMITTER: please set appropriately */
#define CATALOG_VERSION_NO 201111111
#endif

View File

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

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

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

View File

@ -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 */

View File

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

View 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 */

View File

@ -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 */

View File

@ -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);

View File

@ -346,6 +346,7 @@ typedef enum NodeTag
T_ReassignOwnedStmt,
T_CompositeTypeStmt,
T_CreateEnumStmt,
T_CreateRangeStmt,
T_AlterEnumStmt,
T_AlterTSDictionaryStmt,
T_AlterTSConfigurationStmt,

View File

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

View File

@ -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 */

View 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 */

View File

@ -70,6 +70,7 @@ enum SysCacheIdentifier
OPFAMILYOID,
PROCNAMEARGSNSP,
PROCOID,
RANGETYPE,
RELNAMENSP,
RELOID,
RULERELNAME,

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
(p1.typtype not in ('b', 'c', 'd', 'e', 'p')) 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'));

View File

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

View File

@ -18,6 +18,7 @@ test: txid
test: uuid
test: enum
test: money
test: rangetypes
test: strings
test: numerology
test: point

View File

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

View File

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

View File

@ -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]);

View 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;

View File

@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
(p1.typtype not in ('b', 'c', 'd', 'e', 'p')) 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'));