avoid such problems, because of SQL's general
assumption that a null value is a valid value of every data type. Best practice
therefore is to design a domain's constraints so that a null value is allowed,
and then to apply column <literal>NOT NULL</literal> constraints to columns of
the domain type as needed, rather than directly to the domain type.
</para>
<para>
<productname>PostgreSQL</productname> assumes that
<literal>CHECK</literal> constraints' conditions are immutable, that is,
they will always give the same result for the same input value. This
assumption is what justifies examining <literal>CHECK</literal>
constraints only when a value is first converted to be of a domain type,
and not at other times. (This is essentially the same as the treatment
of table <literal>CHECK</literal> constraints, as described in
<xref linkend="ddl-constraints-check-constraints"/>.)
</para>
<para>
An example of a common way to break this assumption is to reference a
user-defined function in a <literal>CHECK</literal> expression, and then
change the behavior of that
function. <productname>PostgreSQL</productname> does not disallow that,
but it will not notice if there are stored values of the domain type that
now violate the <literal>CHECK</literal> constraint. That would cause a
subsequent database dump and restore to fail. The recommended way to
handle such a change is to drop the constraint (using <command>ALTER
DOMAIN</command>), adjust the function definition, and re-add the
constraint, thereby rechecking it against stored data.
</para>
<para>
It's also good practice to ensure that domain <literal>CHECK</literal>
expressions will not throw errors.
</para>
</refsect1>
<refsect1>
<title>Examples</title>
<para>
This example creates the <type>us_postal_code</type> data type and
then uses the type in a table definition. A regular expression test
is used to verify that the value looks like a valid US postal code:
<programlisting>
CREATE DOMAIN us_postal_code AS TEXT
CHECK(
VALUE ~ '^\d{5}$'
OR VALUE ~ '^\d{5}-\d{4}$'
);
CREATE TABLE us_snail_addy (
address_id SERIAL PRIMARY KEY,
street1 TEXT NOT NULL,
street2 TEXT,
street3 TEXT,
city TEXT NOT NULL,
postal us_postal_code NOT NULL
);
</programlisting></para>
</refsect1>
<refsect1 id="sql-createdomain-compatibility">
<title>Compatibility</title>
<para>
The command <command>CREATE DOMAIN</command> conforms to the SQL
standard.
</para>
<para>
The syntax <literal>NOT NULL</literal> in this command is a
<productname>PostgreSQL</productname> extension. (A standard-conforming
way to write the same for non-composite data types would be
<literal>CHECK (VALUE IS NOT
NULL)</literal>. However, per <xref linkend="sql-createdomain-notes"/>,
such constraints are best avoided in practice anyway.) The
<literal>NULL</literal> <quote>constraint</quote> is a
<productname>PostgreSQL</productname> extension (see also <xref
linkend="sql-createtable-compatibility"/>).
</para>
</refsect1>
<refsect1 id="sql-createdomain-see-also">
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-alterdomain"/></member>
<member><xref linkend="sql-dropdomain"/></member>
</simplelist>
</refsect1>
</refentry>