Home Explore Blog CI



postgresql

5th chunk of `doc/src/sgml/rangetypes.sgml`
93de4b2880d64de2c1745643994ad0038d766ca6ffdbed670000000100000fa0
 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>
   function that is aware of the desired step size for the element type.
   The canonicalization function is charged with converting equivalent values
   of the range type to have identical representations, in particular
   consistently inclusive or exclusive bounds.
   If a canonicalization function is not specified, then ranges with different
   formatting will always be treated as unequal, even though they might
   represent the same set of values in reality.
  </para>

  <para>
   The built-in range types <type>int4range</type>, <type>int8range</type>,
   and <type>daterange</type> all use a canonical form that includes
   the lower bound and excludes the upper bound; that is,
   <literal>[)</literal>. User-defined range types 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 over subtypes not provided among the built-in
   range types.
   For example, to define a new range type of subtype <type>float8</type>:

<programlisting>
CREATE TYPE floatrange AS RANGE (
    subtype = float8,
    subtype_diff = float8mi
);

SELECT '[1.234, 5.678]'::floatrange;
</programlisting>

   Because <type>float8</type> has no meaningful
   <quote>step</quote>, we do not define a canonicalization
   function in this example.
  </para>

  <para>
   When you define your own range you automatically get a corresponding
   multirange type.
  </para>

  <para>
   Defining your own range type also allows you to specify a different
   subtype B-tree operator class or collation to use, so as to change the sort
   ordering that determines which values fall into a given range.
  </para>

  <para>
   If the subtype is considered to have discrete rather than continuous
   values, the <command>CREATE TYPE</command> command should specify a
   <literal>canonical</literal> function.
   The canonicalization function takes an input range value, and must return
   an equivalent range value that may have different bounds and formatting.
   The canonical output for two ranges that represent the same set of values,
   for example the integer ranges <literal>[1, 7]</literal> and <literal>[1,
   8)</literal>, must be identical.  It doesn't matter which representation
   you choose to be the canonical one, so long as two equivalent values with
   different formattings are always mapped to the same value with the same
   formatting.  In addition to adjusting the inclusive/exclusive bounds
   format, a canonicalization function might round off boundary values, in
   case the desired step size is larger than what the subtype is capable of
   storing.  For instance, a range type over <type>timestamp</type> could be
   defined to have a step size of an hour, in which case the canonicalization
   function would need to round off bounds that weren't a multiple of an hour,
   or perhaps throw an error

Title: Discrete Range Types and Defining New Range Types
Summary
This section elaborates on discrete range types, emphasizing the importance of a canonicalization function that converts equivalent range values to have identical representations, particularly regarding inclusive/exclusive bounds. It highlights that if no canonicalization function is specified, ranges with different formatting are treated as unequal, even if they represent the same set of values. The built-in range types like int4range, int8range, and daterange use a canonical form that includes the lower bound and excludes the upper bound. The section then explains how users can define their own range types, often to use ranges over subtypes not provided by default. It demonstrates defining a range type for float8 and explains the importance of specifying a canonical function for discrete subtypes, which ensures that equivalent range values are represented identically.