1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Downgrade implicit casts to text to be assignment-only, except for the ones

from the other string-category types; this eliminates a lot of surprising
interpretations that the parser could formerly make when there was no directly
applicable operator.

Create a general mechanism that supports casts to and from the standard string
types (text,varchar,bpchar) for *every* datatype, by invoking the datatype's
I/O functions.  These new casts are assignment-only in the to-string direction,
explicit-only in the other, and therefore should create no surprising behavior.
Remove a bunch of thereby-obsoleted datatype-specific casting functions.

The "general mechanism" is a new expression node type CoerceViaIO that can
actually convert between *any* two datatypes if their external text
representations are compatible.  This is more general than needed for the
immediate feature, but might be useful in plpgsql or other places in future.

This commit does nothing about the issue that applying the concatenation
operator || to non-text types will now fail, often with strange error messages
due to misinterpreting the operator as array concatenation.  Since it often
(not always) worked before, we should either make it succeed or at least give
a more user-friendly error; but details are still under debate.

Peter Eisentraut and Tom Lane
This commit is contained in:
Tom Lane
2007-06-05 21:31:09 +00:00
parent 1120b99445
commit 31edbadf4a
60 changed files with 850 additions and 1612 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/create_cast.sgml,v 1.25 2007/02/01 00:28:18 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/create_cast.sgml,v 1.26 2007/06/05 21:31:04 tgl Exp $ -->
<refentry id="SQL-CREATECAST">
<refmeta>
@ -35,11 +35,11 @@ CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</r
specifies how to perform a conversion between
two data types. For example:
<programlisting>
SELECT CAST(42 AS text);
SELECT CAST(42 AS float8);
</programlisting>
converts the integer constant 42 to type <type>text</type> by
converts the integer constant 42 to type <type>float8</type> by
invoking a previously specified function, in this case
<literal>text(int4)</>. (If no suitable cast has been defined, the
<literal>float8(int4)</>. (If no suitable cast has been defined, the
conversion fails.)
</para>
@ -69,8 +69,7 @@ SELECT CAST(42 AS text);
INSERT INTO foo (f1) VALUES (42);
</programlisting>
will be allowed if the cast from type <type>integer</type> to type
<type>text</type> is marked <literal>AS ASSIGNMENT</>, otherwise
not.
<type>text</type> is marked <literal>AS ASSIGNMENT</>, otherwise not.
(We generally use the term <firstterm>assignment
cast</firstterm> to describe this kind of cast.)
</para>
@ -78,19 +77,37 @@ INSERT INTO foo (f1) VALUES (42);
<para>
If the cast is marked <literal>AS IMPLICIT</> then it can be invoked
implicitly in any context, whether assignment or internally in an
expression. For example, since <literal>||</> takes <type>text</>
operands,
<programlisting>
SELECT 'The time is ' || now();
</programlisting>
will be allowed only if the cast from type <type>timestamp</> to
<type>text</type> is marked <literal>AS IMPLICIT</>. Otherwise it
will be necessary to write the cast explicitly, for example:
<programlisting>
SELECT 'The time is ' || CAST(now() AS text);
</programlisting>
(We generally use the term <firstterm>implicit
expression. (We generally use the term <firstterm>implicit
cast</firstterm> to describe this kind of cast.)
For example, consider this query:
<programlisting>
SELECT 2 + 4.0;
</programlisting>
The parser initially marks the constants as being of type <type>integer</>
and <type>numeric</> respectively. There is no <type>integer</>
<literal>+</> <type>numeric</> operator in the system catalogs,
but there is a <type>numeric</> <literal>+</> <type>numeric</> operator.
The query will therefore succeed if a cast from <type>integer</> to
<type>numeric</> is available and is marked <literal>AS IMPLICIT</> &mdash;
which in fact it is. The parser will apply the implicit cast and resolve
the query as if it had been written
<programlisting>
SELECT CAST ( 2 AS numeric ) + 4.0;
</programlisting>
</para>
<para>
Now, the catalogs also provide a cast from <type>numeric</> to
<type>integer</>. If that cast were marked <literal>AS IMPLICIT</> &mdash;
which it is not &mdash; then the parser would be faced with choosing
between the above interpretation and the alternative of casting the
<type>numeric</> constant to <type>integer</> and applying the
<type>integer</> <literal>+</> <type>integer</> operator. Lacking any
knowledge of which choice to prefer, it would give up and declare the
query ambiguous. The fact that only one of the two casts is
implicit is the way in which we teach the parser to prefer resolution
of a mixed <type>numeric</>-and-<type>integer</> expression as
<type>numeric</>; there is no built-in knowledge about that.
</para>
<para>
@ -208,9 +225,7 @@ SELECT 'The time is ' || CAST(now() AS text);
argument. This is used to represent type-specific length coercion
functions in the system catalogs. The named function is used to
coerce a value of the type to the type modifier value given by its
second argument. (Since the grammar presently permits only certain
built-in data types to have type modifiers, this feature is of no
use for user-defined target types, but we mention it for completeness.)
second argument.
</para>
<para>
@ -237,6 +252,32 @@ SELECT 'The time is ' || CAST(now() AS text);
need to declare casts both ways explicitly.
</para>
<indexterm zone="sql-createcast">
<primary>cast</primary>
<secondary>I/O conversion</secondary>
</indexterm>
<para>
It is normally not necessary to create casts between user-defined types
and the standard string types (<type>text</>, <type>varchar</>, and
<type>char(<replaceable>n</>)</type>). <productname>PostgreSQL</> will
automatically handle a cast to a string type by invoking the other
type's output function, or conversely handle a cast from a string type
by invoking the other type's input function. These
automatically-provided casts are known as <firstterm>I/O conversion
casts</>. I/O conversion casts to string types are treated as
assignment casts, while I/O conversion casts from string types are
explicit-only. You can override this behavior by declaring your own
cast to replace an I/O conversion cast, but usually the only reason to
do so is if you want the conversion to be more easily invokable than the
standard assignment-only or explicit-only setting. Another possible
reason is that you want the conversion to behave differently from the
type's I/O function; but that is sufficiently surprising that you
should think twice about whether it's a good idea. (A small number of
the built-in types do indeed have different behaviors for conversions,
mostly because of requirements of the SQL standard.)
</para>
<para>
Prior to <productname>PostgreSQL</> 7.3, every function that had
the same name as a data type, returned that data type, and took one
@ -265,16 +306,20 @@ SELECT 'The time is ' || CAST(now() AS text);
<note>
<para>
There is one small lie in the preceding paragraph: there is still one
case in which <structname>pg_cast</> will be used to resolve the
meaning of an apparent function call. If a
function call <replaceable>name</>(<replaceable>x</>) matches no
actual function, but <replaceable>name</> is the name of a data type
and <structname>pg_cast</> shows a binary-compatible cast to this
type from the type of <replaceable>x</>, then the call will be construed
as an explicit cast. This exception is made so that binary-compatible
casts can be invoked using functional syntax, even though they lack
any function.
Actually the preceding paragraph is an oversimplification: there are
two cases in which a function-call construct will be treated as a cast
request without having matched it to an actual function.
If a function call <replaceable>name</>(<replaceable>x</>) does not
exactly match any existing function, but <replaceable>name</> is the name
of a data type and <structname>pg_cast</> provides a binary-compatible cast
to this type from the type of <replaceable>x</>, then the call will be
construed as a binary-compatible cast. This exception is made so that
binary-compatible casts can be invoked using functional syntax, even
though they lack any function. Likewise, if there is no
<structname>pg_cast</> entry but the cast would be to or from a string
type, the call will be construed as an I/O conversion cast. This
exception allows I/O conversion casts to be invoked using functional
syntax.
</para>
</note>
</refsect1>
@ -284,10 +329,10 @@ SELECT 'The time is ' || CAST(now() AS text);
<title>Examples</title>
<para>
To create a cast from type <type>text</type> to type
<type>int4</type> using the function <literal>int4(text)</literal>:
To create a cast from type <type>bigint</type> to type
<type>int4</type> using the function <literal>int4(bigint)</literal>:
<programlisting>
CREATE CAST (text AS int4) WITH FUNCTION int4(text);
CREATE CAST (bigint AS int4) WITH FUNCTION int4(bigint);
</programlisting>
(This cast is already predefined in the system.)
</para>