Documentation improvements around domain types.

I was a bit surprised to find that domains were almost completely
unmentioned in the main SGML documentation, outside of the reference
pages for CREATE/ALTER/DROP DOMAIN.  In particular, noplace was it
mentioned that we don't support domains over composite, making it
hard to document the planned fix for that.

Hence, add a section about domains to chapter 8 (Data Types).

Also, modernize the type system overview in section 37.2; it had never
heard of range types, and insisted on calling arrays base types, which
seems a bit odd from a user's perspective; furthermore it didn't fit well
with the fact that we now support arrays over types other than base types.
It seems appropriate to use the term "container types" to describe all of
arrays, composites, and ranges, so let's do that.

Also a few other minor improvements, notably improve an example query
in rowtypes.sgml by using a LATERAL function instead of an ad-hoc
OFFSET 0 clause.

In part this is mop-up for commit c12d570fa, which missed updating 37.2
to reflect the fact that it added arrays of domains.  We could possibly
back-patch this without that claim, but I don't feel a strong need to.
This commit is contained in:
Tom Lane 2017-10-24 14:08:40 -04:00
parent 8df4ce1eac
commit a32c0923b4
4 changed files with 124 additions and 34 deletions

View File

@ -4359,6 +4359,60 @@ SET xmloption TO { DOCUMENT | CONTENT };
&rangetypes;
<sect1 id="domains">
<title>Domain Types</title>
<indexterm zone="domains">
<primary>domain</primary>
</indexterm>
<indexterm zone="domains">
<primary>data type</primary>
<secondary>domain</secondary>
</indexterm>
<para>
A <firstterm>domain</firstterm> is a user-defined data type that is
based on another <firstterm>underlying type</firstterm>. Optionally,
it can have constraints that restrict its valid values to a subset of
what the underlying type would allow. Otherwise it behaves like the
underlying type &mdash; for example, any operator or function that
can be applied to the underlying type will work on the domain type.
The underlying type can be any built-in or user-defined base type,
enum type, array or range type, or another domain. (Currently, domains
over composite types are not implemented.)
</para>
<para>
For example, we could create a domain over integers that accepts only
positive integers:
<programlisting>
CREATE DOMAIN posint AS integer CHECK (VALUE &gt; 0);
CREATE TABLE mytable (id posint);
INSERT INTO mytable VALUES(1); -- works
INSERT INTO mytable VALUES(-1); -- fails
</programlisting>
</para>
<para>
When an operator or function of the underlying type is applied to a
domain value, the domain is automatically down-cast to the underlying
type. Thus, for example, the result of <literal>mytable.id - 1</literal>
is considered to be of type <type>integer</type> not <type>posint</type>.
We could write <literal>(mytable.id - 1)::posint</literal> to cast the
result back to <type>posint</type>, causing the domain's constraints
to be rechecked. In this case, that would result in an error if the
expression had been applied to an <structfield>id</structfield> value of
1. Assigning a value of the underlying type to a field or variable of
the domain type is allowed without writing an explicit cast, but the
domain's constraints will be checked.
</para>
<para>
For additional information see <xref linkend="sql-createdomain">.
</para>
</sect1>
<sect1 id="datatype-oid">
<title>Object Identifier Types</title>

View File

@ -106,31 +106,60 @@
<secondary>composite</secondary>
</indexterm>
<indexterm zone="extend-type-system">
<primary>container type</primary>
</indexterm>
<indexterm zone="extend-type-system">
<primary>data type</primary>
<secondary>container</secondary>
</indexterm>
<para>
<productname>PostgreSQL</productname> data types are divided into base
types, composite types, domains, and pseudo-types.
<productname>PostgreSQL</productname> data types can be divided into base
types, container types, domains, and pseudo-types.
</para>
<sect2>
<title>Base Types</title>
<para>
Base types are those, like <type>int4</type>, that are
Base types are those, like <type>integer</type>, that are
implemented below the level of the <acronym>SQL</acronym> language
(typically in a low-level language such as C). They generally
correspond to what are often known as abstract data types.
<productname>PostgreSQL</productname> can only operate on such
types through functions provided by the user and only understands
the behavior of such types to the extent that the user describes
them. Base types are further subdivided into scalar and array
types. For each scalar type, a corresponding array type is
automatically created that can hold variable-size arrays of that
scalar type.
them.
The built-in base types are described in <xref linkend="datatype">.
</para>
<para>
Enumerated (enum) types can be considered as a subcategory of base
types. The main difference is that they can be created using
just <acronym>SQL</acronym> commands, without any low-level programming.
Refer to <xref linkend="datatype-enum"> for more information.
</para>
</sect2>
<sect2>
<title>Composite Types</title>
<title>Container Types</title>
<para>
<productname>PostgreSQL</productname> has three kinds
of <quote>container</quote> types, which are types that contain multiple
values of other types. These are arrays, composites, and ranges.
</para>
<para>
Arrays can hold multiple values that are all of the same type. An array
type is automatically created for each base type, composite type, range
type, and domain type. But there are no arrays of arrays. So far as
the type system is concerned, multi-dimensional arrays are the same as
one-dimensional arrays. Refer to <xref linkend="arrays"> for more
information.
</para>
<para>
Composite types, or row types, are created whenever the user
@ -139,9 +168,15 @@
define a <quote>stand-alone</quote> composite type with no associated
table. A composite type is simply a list of types with
associated field names. A value of a composite type is a row or
record of field values. The user can access the component fields
from <acronym>SQL</acronym> queries. Refer to <xref linkend="rowtypes">
for more information on composite types.
record of field values. Refer to <xref linkend="rowtypes">
for more information.
</para>
<para>
A range type can hold two values of the same type, which are the lower
and upper bounds of the range. Range types are user-created, although
a few built-in ones exist. Refer to <xref linkend="rangetypes">
for more information.
</para>
</sect2>
@ -149,16 +184,12 @@
<title>Domains</title>
<para>
A domain is based on a particular base type and for many purposes
is interchangeable with its base type. However, a domain can
have constraints that restrict its valid values to a subset of
what the underlying base type would allow.
</para>
<para>
Domains can be created using the <acronym>SQL</acronym> command
<xref linkend="sql-createdomain">.
Their creation and use is not discussed in this chapter.
A domain is based on a particular underlying type and for many purposes
is interchangeable with its underlying type. However, a domain can have
constraints that restrict its valid values to a subset of what the
underlying type would allow. Domains are created using
the <acronym>SQL</acronym> command <xref linkend="sql-createdomain">.
Refer to <xref linkend="domains"> for more information.
</para>
</sect2>
@ -167,8 +198,8 @@
<para>
There are a few <quote>pseudo-types</quote> for special purposes.
Pseudo-types cannot appear as columns of tables or attributes of
composite types, but they can be used to declare the argument and
Pseudo-types cannot appear as columns of tables or components of
container types, but they can be used to declare the argument and
result types of functions. This provides a mechanism within the
type system to identify special classes of functions. <xref
linkend="datatype-pseudotypes-table"> lists the existing
@ -188,7 +219,7 @@
</indexterm>
<indexterm zone="extend-types-polymorphic">
<primary>type</primary>
<primary>data type</primary>
<secondary>polymorphic</secondary>
</indexterm>

View File

@ -199,7 +199,7 @@ ALTER DOMAIN <replaceable class="parameter">name</replaceable>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">NOT VALID</replaceable></term>
<term><literal>NOT VALID</literal></term>
<listitem>
<para>
Do not verify existing column data for constraint validity.
@ -274,11 +274,11 @@ ALTER DOMAIN <replaceable class="parameter">name</replaceable>
<para>
Currently, <command>ALTER DOMAIN ADD CONSTRAINT</command>, <command>ALTER
DOMAIN VALIDATE CONSTRAINT</command>, and <command>ALTER DOMAIN SET NOT NULL</command>
will fail if the validated named domain or
any derived domain is used within a composite-type column of any
table in the database. They should eventually be improved to be
able to verify the new constraint for such nested columns.
DOMAIN VALIDATE CONSTRAINT</command>, and <command>ALTER DOMAIN SET NOT
NULL</command> will fail if the named domain or any derived domain is used
within a container-type column (a composite, array, or range column) in
any table in the database. They should eventually be improved to be able
to verify the new constraint for such nested values.
</para>
</refsect1>

View File

@ -328,11 +328,16 @@ SELECT (myfunc(x)).a, (myfunc(x)).b, (myfunc(x)).c FROM some_table;
with either syntax. If it's an expensive function you may wish to
avoid that, which you can do with a query like:
<programlisting>
SELECT (m).* FROM (SELECT myfunc(x) AS m FROM some_table OFFSET 0) ss;
SELECT m.* FROM some_table, LATERAL myfunc(x) AS m;
</programlisting>
The <literal>OFFSET 0</literal> clause keeps the optimizer
from <quote>flattening</quote> the sub-select to arrive at the form with
multiple calls of <function>myfunc()</function>.
Placing the function in
a <literal>LATERAL</literal> <literal>FROM</literal> item keeps it from
being invoked more than once per row. <literal>m.*</literal> is still
expanded into <literal>m.a, m.b, m.c</literal>, but now those variables
are just references to the output of the <literal>FROM</literal> item.
(The <literal>LATERAL</literal> keyword is optional here, but we show it
to clarify that the function is getting <structfield>x</structfield>
from <structname>some_table</structname>.)
</para>
</tip>