CREATE CAST — definir una nueva conversión (cast)
CREATE CAST (source_typeAStarget_type) WITH FUNCTIONfunction_name[ (argument_type[, ...]) ] [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (source_typeAStarget_type) WITHOUT FUNCTION [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (source_typeAStarget_type) WITH INOUT [ AS ASSIGNMENT | AS IMPLICIT ]
CREATE CAST define una nueva conversión (cast). Una conversión
especifica cómo realizar una transformación entre dos tipos de datos. Por ejemplo,
SELECT CAST(42 AS float8);
convierte la constante entera 42 al tipo float8 invocando una función
especificada previamente, en este caso float8(int4). (Si no se ha
definido ninguna conversión adecuada, la transformación falla).
Dos tipos pueden ser coercibles binariamente (binary coercible), lo
que significa que la conversión se puede realizar «gratis», sin invocar ninguna función. Esto
requiere que los valores correspondientes utilicen la misma representación interna. Por ejemplo,
los tipos text y varchar son coercibles binariamente en ambas direcciones.
La coercibilidad binaria no es necesariamente una relación simétrica. Por ejemplo, la conversión
de xml a text se puede realizar de forma gratuita en la implementación actual,
pero la dirección inversa requiere una función que realice al menos una comprobación sintáctica.
(Dos tipos que son coercibles binariamente en ambas direcciones también se denominan compatibles
binariamente).
Puedes definir una conversión como una conversión de conversión de E/S (I/O conversion cast)
utilizando la sintaxis WITH INOUT. Una conversión de E/S se realiza invocando la función de
salida (output function) del tipo de datos de origen y pasando la cadena resultante a la función de entrada
(input function) del tipo de datos de destino. En muchos casos comunes, esta característica evita la necesidad de
escribir una función de conversión separada. Una conversión de E/S actúa igual que una conversión regular basada
en una función; solo la implementación es diferente.
Por defecto, una conversión solo se puede invocar mediante una solicitud de conversión explícita, esto es, un constructo
explícito CAST( o
x AS typename)x::typename.
Si la conversión está marcada como AS ASSIGNMENT, entonces se puede invocar implícitamente al asignar
un valor a una columna del tipo de datos de destino. Por ejemplo, suponiendo que foo.f1 es una
columna de tipo text, entonces:
INSERT INTO foo (f1) VALUES (42);
estará permitido si la conversión del tipo integer al tipo text está marcada como
AS ASSIGNMENT; de lo contrario, no. (Generalmente usamos el término conversión de
asignación [assignment cast] para describir este tipo de conversión).
Si la conversión está marcada como AS IMPLICIT, entonces se puede invocar implícitamente en cualquier
contexto, ya sea de asignación o internamente en una expresión. (Generalmente usamos el término conversión
implícita [implicit cast] para describir este tipo de conversión). Por ejemplo, considera esta consulta:
SELECT 2 + 4.0;
El analizador sintáctico (parser) inicialmente marca las constantes como de tipo integer y numeric
respectivamente. No existe un operador integer + numeric en los catálogos del
sistema, pero existe un operador numeric + numeric. Por lo tanto, la consulta
tendrá éxito si hay disponible una conversión de integer a numeric y está marcada como
AS IMPLICIT — lo cual de hecho ocurre. El parser aplicará la conversión implícita y resolverá
la consulta como si se hubiera escrito:
SELECT CAST ( 2 AS numeric ) + 4.0;
Ahora bien, los catálogos también proporcionan una conversión de numeric a integer. Si esa
conversión estuviera marcada como AS IMPLICIT — lo cual no es así — entonces el parser se
enfrentaría a elegir entre la interpretación anterior y la alternativa de convertir la constante numeric a
integer y aplicar el operador integer + integer. Al no tener conocimiento
de qué opción preferir, desistiría y declararía la consulta como ambigua. El hecho de que solo una de las dos conversiones sea
implícita es la forma en que le enseñamos al parser a preferir la resolución de una expresión mixta de
numeric e integer como numeric; no hay un conocimiento integrado sobre eso.
Es prudente ser conservador al marcar las conversiones como implícitas. Una sobreabundancia de rutas de conversión implícitas
puede hacer que PostgreSQL elija interpretaciones sorprendentes de los comandos, o que sea incapaz de
resolver los comandos por completo debido a que existen múltiples interpretaciones posibles. Una buena regla general es hacer
que una conversión sea implícitamente invocable únicamente para transformaciones que preserven la información entre tipos de la
misma categoría de tipo general. Por ejemplo, la conversión de int2 a int4 puede ser razonablemente
implícita, pero la conversión de float8 a int4 probablemente debería ser solo de asignación. Las
conversiones entre distintas categorías de tipos, como text a int4, es mejor que sean exclusivamente explícitas.
A veces es necesario, por razones de usabilidad o de cumplimiento de estándares, proporcionar múltiples conversiones implícitas entre un conjunto de tipos, lo que resulta en una ambigüedad que no se puede evitar como anteriormente. El parser tiene una heurística de respaldo basada en categorías de tipos (type categories) y tipos preferidos (preferred types) que puede ayudar a proporcionar el comportamiento deseado en tales casos. Consulta CREATE TYPE para obtener más información.
Para poder crear una conversión, debes ser propietario del tipo de datos de origen o de destino y tener el privilegio
USAGE en el otro tipo. Para crear una conversión coercible binariamente, debes ser superusuario. (Esta restricción
se realiza porque una conversión coercible binariamente errónea puede hacer caer fácilmente al servidor).
source_typeEl nombre del tipo de datos origen de la conversión.
target_typeEl nombre del tipo de datos destino de la conversión.
function_name[(argument_type [, ...])]La función utilizada para realizar la conversión. El nombre de la función puede estar calificado por el esquema. Si no lo está, la función se buscará en la ruta de búsqueda del esquema. El tipo de datos del resultado de la función debe coincidir con el tipo de destino de la conversión. Sus argumentos se analizan a continuación. Si no se especifica una lista de argumentos, el nombre de la función debe ser único en su esquema.
WITHOUT FUNCTIONIndica que el tipo origen es coercible binariamente al tipo de destino, por lo que no se requiere ninguna función para realizar la conversión.
WITH INOUTIndica que la conversión es una conversión de E/S, realizada invocando la función de salida del tipo de datos origen y pasando la cadena resultante a la función de entrada del tipo de datos destino.
AS ASSIGNMENTIndica que la conversión se puede invocar implícitamente en contextos de asignación.
AS IMPLICITIndica que la conversión se puede invocar implícitamente en cualquier contexto.
Las funciones de implementación de conversiones pueden tener de uno a tres argumentos. El tipo del primer argumento debe ser idéntico
o coercible binariamente desde el tipo origen de la conversión. El segundo argumento, si está presente, debe ser de tipo
integer; recibe el modificador de tipo asociado con el tipo de destino, o -1 si no hay ninguno.
El tercer argumento, si está presente, debe ser de tipo boolean; recibe true si la conversión es una
conversión explícita, o false de lo contrario. (Curiosamente, el estándar SQL exige comportamientos diferentes
para las conversiones explícitas e implícitas en algunos casos. Este argumento se proporciona para las funciones que deben implementar
tales conversiones. No se recomienda diseñar tus propios tipos de datos para que esto importe).
El tipo de retorno de una función de conversión debe ser idéntico o coercible binariamente al tipo de destino de la conversión.
Normalmente, una conversión debe tener diferentes tipos de datos origen y destino. Sin embargo, está permitido declarar una conversión con tipos de origen y destino idénticos si tiene una función de implementación de conversión con más de un argumento. Esto se utiliza para representar funciones de coerción de longitud específicas del tipo en los catálogos del sistema. La función con nombre se utiliza para forzar un valor del tipo al valor del modificador de tipo dado por su segundo argumento.
Cuando una conversión tiene tipos de origen y destino diferentes y una función que toma más de un argumento, admite la conversión de un tipo a otro y la aplicación de una coerción de longitud en un solo paso. Cuando no existe tal entrada, la coerción a un tipo que utiliza un modificador de tipo implica dos pasos de conversión, uno para convertir entre tipos de datos y un segundo para aplicar el modificador.
Una conversión hacia o desde un tipo de dominio actualmente no tiene efecto. La conversión hacia o desde un dominio utiliza las conversiones asociadas con su tipo subyacente.
Usa DROP CAST para eliminar conversiones definidas por el usuario.
Recuerda que si quieres poder convertir tipos en ambas direcciones, necesitas declarar las conversiones en ambas direcciones explícitamente.
Normalmente no es necesario crear conversiones entre los tipos definidos por el usuario y los tipos de cadena estándar (text,
varchar y char(, así como los tipos definidos por el usuario que se definen como de
la categoría de cadena). PostgreSQL proporciona conversiones automáticas de conversión de E/S para ello.
Las conversiones automáticas a tipos de cadena se tratan como conversiones de asignación, mientras que las conversiones automáticas desde
tipos de cadena son exclusivamente explícitas. Puedes anular este comportamiento declarando tu propia conversión para reemplazar una
conversión automática, pero generalmente la única razón para hacerlo es si deseas que la conversión sea más fácil de invocar que la
configuración estándar de solo asignación o solo explícita. Otra razón posible es que desees que la conversión se comporte de manera diferente
a la función de E/S del tipo; pero eso es lo suficientemente sorprendente como para que pienses dos veces si es una buena idea. (Un pequeño
número de tipos incorporados tienen comportamientos diferentes para las conversiones, principalmente debido a los requisitos del estándar SQL).
n)
Aunque no es obligatorio, se recomienda que continúes siguiendo esta antigua convención de nombrar las funciones de implementación de
conversiones de la misma manera que el tipo de datos de destino. Muchos usuarios están acostumbrados a poder convertir tipos de datos utilizando
una notación de estilo de función, es decir, typename(x). Esta notación es, de hecho, ni
más ni menos que una llamada a la función de implementación de la conversión; no se trata especialmente como una conversión. Si tus funciones
de conversión no están nombradas para admitir esta convención, sorprenderás a los usuarios. Dado que PostgreSQL
permite la sobrecarga del mismo nombre de función con diferentes tipos de argumentos, no hay dificultad en tener múltiples funciones de conversión
desde diferentes tipos que utilicen todas el nombre del tipo de destino.
En realidad, el párrafo anterior es una simplificación excesiva: hay dos casos en los que un constructo de llamada a función se tratará como
una solicitud de conversión sin haberlo emparejado con una función real. Si una llamada a función name(x)
no coincide exactamente con ninguna función existente, pero name es el nombre de un tipo de datos y
pg_cast proporciona una conversión coercible binariamente a este tipo desde el tipo de x, entonces
la llamada se interpretará como una conversión coercible binariamente. Esta excepción se hace para que las conversiones coercibles binariamente
se puedan invocar utilizando sintaxis funcional, a pesar de que carecen de cualquier función. Del mismo modo, si no hay ninguna entrada en
pg_cast pero la conversión fuera hacia o desde un tipo de cadena, la llamada se interpretará como una conversión de E/S.
Esta excepción permite que las conversiones de E/S se invoquen utilizando la sintaxis funcional.
También hay una excepción a la excepción: las conversiones de E/S de tipos compuestos a tipos de cadena no se pueden invocar utilizando la
sintaxis funcional, sino que deben escribirse en la sintaxis de conversión explícita (ya sea con la notación CAST o
::). Esta excepción se añadió porque, tras la introducción de las conversiones de E/S proporcionadas automáticamente, se descubrió
que era demasiado fácil invocar accidentalmente una conversión de este tipo cuando se pretendía hacer referencia a una función o columna.
Para crear una conversión de asignación del tipo bigint al tipo int4 utilizando la función
int4(bigint):
CREATE CAST (bigint AS int4) WITH FUNCTION int4(bigint) AS ASSIGNMENT;
(Esta conversión ya está predefinida en el sistema).
El comando CREATE CAST cumple con el estándar SQL, excepto que SQL no contempla tipos coercibles binariamente
ni argumentos adicionales para las funciones de implementación. AS IMPLICIT también es una extensión de
PostgreSQL.