'[4,4)'::int4range;
</programlisting>
</para>
<para>
The input for a multirange is curly brackets (<literal>{</literal> and
<literal>}</literal>) containing zero or more valid ranges,
separated by commas. Whitespace is permitted around the brackets and
commas. This is intended to be reminiscent of array syntax, although
multiranges are much simpler: they have just one dimension and there is
no need to quote their contents. (The bounds of their ranges may be
quoted as above however.)
</para>
<para>
Examples:
<programlisting>
SELECT '{}'::int4multirange;
SELECT '{[3,7)}'::int4multirange;
SELECT '{[3,7), [8,9)}'::int4multirange;
</programlisting>
</para>
</sect2>
<sect2 id="rangetypes-construct">
<title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
type. Using the constructor function is frequently more convenient than
writing a range literal constant, since it avoids the need for extra
quoting of the bound values. The constructor function
accepts two or three arguments. The two-argument form constructs a range
in standard form (lower bound inclusive, upper bound exclusive), while
the three-argument form constructs a range with bounds of the form
specified by the third argument.
The third argument must be one of the strings
<quote><literal>()</literal></quote>,
<quote><literal>(]</literal></quote>,
<quote><literal>[)</literal></quote>, or
<quote><literal>[]</literal></quote>.
For example:
<programlisting>
-- The full form is: lower bound, upper bound, and text argument indicating
-- inclusivity/exclusivity of bounds.
SELECT numrange(1.0, 14.0, '(]');
-- If the third argument is omitted, '[)' is assumed.
SELECT numrange(1.0, 14.0);
-- Although '(]' is specified here, on display the value will be converted to
-- canonical form, since int8range is a discrete range type (see below).
SELECT int8range(1, 14, '(]');
-- Using NULL for either bound causes the range to be unbounded on that side.
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
<para>
Each range type also has a multirange constructor with the same name as the
multirange type. The constructor function takes zero or more arguments
which are all ranges of the appropriate type.
For example:
<programlisting>
SELECT nummultirange();
SELECT nummultirange(numrange(1.0, 14.0));
SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
</programlisting>
</para>
</sect2>
<sect2 id="rangetypes-discrete">
<title>Discrete Range Types</title>
<para>
A discrete range is one whose element type has a well-defined
<quote>step</quote>, such as <type>integer</type> or <type>date</type>.
In these types two elements can be said to be adjacent, when there are
no valid values between them. This contrasts with continuous ranges,
where it's always (or almost always) possible to identify other element
values between two given values. For example, a range over the
<type>numeric</type> type is continuous, as is a range over <type>timestamp</type>.
(Even though <type>timestamp</type> has limited precision, and so could
theoretically be treated as discrete, it's better to consider it continuous
since the step size is normally not of interest.)
</para>
<para>
Another way to think about a discrete range type is that there is a clear
idea of a <quote>next</quote> or <quote>previous</quote> value for each element value.
Knowing that, it is possible to convert between inclusive and exclusive
representations of a range's bounds, by choosing the next or previous
element value instead of the one originally given.
For example, in an integer range type <literal>[4,8]</literal> and
<literal>(3,9)</literal> denote the same set of values; but this would not be so
for a range over numeric.
</para>
<para>
A discrete range type should have a <firstterm>canonicalization</firstterm>