<para>
<command>CREATE CAST</command> defines a new cast. A cast
specifies how to perform a conversion between
- two data types. For example:
+ two data types. For example,
<programlisting>
SELECT CAST(42 AS float8);
</programlisting>
</para>
<para>
- You can define a cast as an <firstterm>I/O conversion cast</> using
+ You can define a cast as an <firstterm>I/O conversion cast</> by using
the <literal>WITH INOUT</literal> syntax. An I/O conversion cast is
performed by invoking the output function of the source data type, and
- passing the result to the input function of the target data type.
+ passing the resulting string to the input function of the target data type.
+ In many common cases, this feature avoids the need to write a separate
+ cast function for conversion. An I/O conversion cast acts the same as
+ a regular function-based cast; only the implementation is different.
</para>
<para>
<para>
Indicates that the cast is an I/O conversion cast, performed by
invoking the output function of the source data type, and passing the
- result to the input function of the target data type.
+ resulting string to the input function of the target data type.
</para>
</listitem>
</varlistentry>
<para>
When a cast has different source and
target types and a function that takes more than one argument, it
- represents converting from one type to another and applying a length
+ supports converting from one type to another and applying a length
coercion in a single step. When no such entry is available, coercion
- to a type that uses a type modifier involves two steps, one to
+ to a type that uses a type modifier involves two cast steps, one to
convert between data types and a second to apply the modifier.
</para>
syntax.
</para>
</note>
+
+ <note>
+ <para>
+ There's an exception to the exception, too: I/O conversion casts from
+ composite types to string types cannot be invoked using functional
+ syntax, but must be written in explicit cast syntax (either
+ <literal>CAST</> or <literal>::</> notation). This exception was added
+ because after the introduction of automatically-provided I/O conversion
+ casts, it was found too easy to accidentally invoke such a cast when
+ a function or column reference was intended.
+ </para>
+ </note>
</refsect1>
The arguments can optionally have names attached.
See <xref linkend="sql-syntax-calling-funcs"> for details.
</para>
+
+ <note>
+ <para>
+ A function that takes a single argument of composite type can
+ optionally be called using field-selection syntax, and conversely
+ field selection can be written in functional style. That is, the
+ notations <literal>col(table)</> and <literal>table.col</> are
+ interchangeable. This behavior is not SQL-standard but is provided
+ in <productname>PostgreSQL</> because it allows use of functions to
+ emulate <quote>computed fields</>. For more information see
+ <xref linkend="xfunc-sql-composite-functions">.
+ </para>
+ </note>
</sect2>
<sect2 id="syntax-aggregates">
</para>
</sect2>
- <sect2>
+ <sect2 id="xfunc-sql-composite-functions">
<title><acronym>SQL</acronym> Functions on Composite Types</title>
<para>
<literal>double_salary</> isn't a real column of the table.
(You can also emulate computed fields with views.)
</para>
+
+ <para>
+ Because of this behavior, it's unwise to give a function that takes
+ a single composite-type argument the same name as any of the fields of
+ that composite type.
+ </para>
</tip>
<para>
* can't write "foo[] (something)" as a function call. In theory
* someone might want to invoke it as "_foo (something)" but we have
* never supported that historically, so we can insist that people
- * write it as a normal cast instead. Lack of historical support is
- * also the reason for not considering composite-type casts here.
+ * write it as a normal cast instead.
+ *
+ * We also reject the specific case of COERCEVIAIO for a composite
+ * source type and a string-category target type. This is a case that
+ * find_coercion_pathway() allows by default, but experience has shown
+ * that it's too commonly invoked by mistake. So, again, insist that
+ * people use cast syntax if they want to do that.
*
* NB: it's important that this code does not exceed what coerce_type
* can do, because the caller will try to apply coerce_type if we
cpathtype = find_coercion_pathway(targetType, sourceType,
COERCION_EXPLICIT,
&cfuncid);
- iscoercion = (cpathtype == COERCION_PATH_RELABELTYPE ||
- cpathtype == COERCION_PATH_COERCEVIAIO);
+ switch (cpathtype)
+ {
+ case COERCION_PATH_RELABELTYPE:
+ iscoercion = true;
+ break;
+ case COERCION_PATH_COERCEVIAIO:
+ if ((sourceType == RECORDOID ||
+ ISCOMPLEX(sourceType)) &&
+ TypeCategory(targetType) == TYPCATEGORY_STRING)
+ iscoercion = false;
+ else
+ iscoercion = true;
+ break;
+ default:
+ iscoercion = false;
+ break;
+ }
}
if (iscoercion)
(3 rows)
rollback;
+--
+-- We allow I/O conversion casts from composite types to strings to be
+-- invoked via cast syntax, but not functional syntax. This is because
+-- the latter is too prone to be invoked unintentionally.
+--
+select cast (fullname as text) from fullname;
+ fullname
+----------
+(0 rows)
+
+select fullname::text from fullname;
+ fullname
+----------
+(0 rows)
+
+select text(fullname) from fullname; -- error
+ERROR: function text(fullname) does not exist
+LINE 1: select text(fullname) from fullname;
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select fullname.text from fullname; -- error
+ERROR: column fullname.text does not exist
+LINE 1: select fullname.text from fullname;
+ ^
+-- same, but RECORD instead of named composite type:
+select cast (row('Jim', 'Beam') as text);
+ row
+------------
+ (Jim,Beam)
+(1 row)
+
+select (row('Jim', 'Beam'))::text;
+ row
+------------
+ (Jim,Beam)
+(1 row)
+
+select text(row('Jim', 'Beam')); -- error
+ERROR: function text(record) does not exist
+LINE 1: select text(row('Jim', 'Beam'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select (row('Jim', 'Beam')).text; -- error
+ERROR: could not identify column "text" in record data type
+LINE 1: select (row('Jim', 'Beam')).text;
+ ^
select * from price;
rollback;
+
+--
+-- We allow I/O conversion casts from composite types to strings to be
+-- invoked via cast syntax, but not functional syntax. This is because
+-- the latter is too prone to be invoked unintentionally.
+--
+select cast (fullname as text) from fullname;
+select fullname::text from fullname;
+select text(fullname) from fullname; -- error
+select fullname.text from fullname; -- error
+-- same, but RECORD instead of named composite type:
+select cast (row('Jim', 'Beam') as text);
+select (row('Jim', 'Beam'))::text;
+select text(row('Jim', 'Beam')); -- error
+select (row('Jim', 'Beam')).text; -- error