CREATE CAST (tipo_de_origem AS tipo_de_destino) WITH FUNCTION nome_da_função (tipos_dos_argumentos) [ AS ASSIGNMENT | AS IMPLICIT ] CREATE CAST (tipo_de_origem AS tipo_de_destino) WITHOUT FUNCTION [ AS ASSIGNMENT | AS IMPLICIT ]
O comando CREATE CAST cria uma conversão. A conversão especifica como realizar a conversão entre dois tipos de dado. Por exemplo,
SELECT CAST(42 AS text);
converte a constante inteira 42 para o tipo text chamando uma função especificada previamente, neste caso text(int4); se não tiver sido definida nenhuma conversão adequada, a conversão falhará.
Dois tipos podem ser binariamente compatíveis, significando que podem ser convertidos um no outro "diretamente", sem chamar nenhuma função. Requer que os valores correspondentes utilizem a mesma representação interna. Por exemplo, os tipos text e varchar são binariamente compatíveis.
Por padrão, a conversão somente pode ser feita por uma solicitação de conversão explícita, ou seja, uma construção explícita CAST(x AS nome_do_tipo) ou x::nome_do_tipo.
Se a conversão for marcada como AS ASSIGNMENT então poderá ser chamada implicitamente quando o valor for atribuído a uma coluna com o tipo de dado de destino. Por exemplo, supondo que foo.f1 seja uma coluna do tipo text, então a atribuição
INSERT INTO foo (f1) VALUES (42);
será permitida se a conversão do tipo integer para o tipo text estiver marcada como AS ASSIGNMENT, caso contrário a atribuição não será permitida; geralmente é utilizado o termo conversão de atribuição (assignment cast) para descrever este tipo de conversão.
Se a conversão estiver marcada como AS IMPLICIT então poderá ser chamada implicitamente em qualquer contexto, seja em uma atribuição ou internamente em uma expressão. Por exemplo, como || recebe operandos do tipo text, então a expressão
SELECT 'Data e hora: ' || now();
somente será permitida se a conversão do tipo timestamp para o tipo text estiver marcada como AS IMPLICIT, senão será necessário escrever a conversão explicitamente como, por exemplo,
SELECT 'Data e hora: ' || CAST(now() AS text);
(Geralmente é utilizado o termo conversão implícita (implicit cast) para descrever este tipo de conversão).
É aconselhável ser conservador com relação a marcar as conversões como implícitas. Uma superabundância de possibilidades de conversões implícitas poderá fazer com que o PostgreSQL escolha interpretações surpreendentes para os comandos, ou até que não consiga resolver o comando devido à existência de várias interpretações possíveis. Uma boa regra empírica é tornar a conversão chamável implicitamente somente no caso das transformações que preservam as informações entre tipos da mesma categoria geral. Por exemplo, faz sentido que a conversão de int2 para int4 seja implícita, mas a conversão de float8 para int4 provavelmente deve ser somente de atribuição. Conversões entre tipos de categorias diferentes, como text para int4, é melhor que sejam somente explícitas.
É necessário ser o dono do tipo de dado de origem ou de destino para poder criar uma conversão. Para criar uma conversão binariamente compatível é necessário ser um superusuário; esta restrição é feita porque uma conversão binariamente compatível errada poderá facilmente derrubar o servidor.
O nome do tipo de dado de origem da conversão.
O nome do tipo de dado de destino da conversão.
A função utilizada para realizar a conversão. O nome da função pode ser qualificado pelo esquema. Se não for, a função será procurada no caminho de procura de esquema. O tipo de dado do resultado da função deve corresponder ao tipo de dado de destino da conversão. Os argumentos são discutidos abaixo.
Indica que o tipo de dado de origem e o tipo de dado de destino são binariamente compatíveis e, por isso, não é necessária nenhuma função para realizar a conversão.
Indica que a conversão pode ser chamada implicitamente em contextos de atribuição.
Indica que a conversão pode ser chamada implicitamente em qualquer contexto.
As funções de implementação de conversão podem ter de um a três argumentos. O tipo do primeiro argumento deve ser idêntico ao tipo de origem da conversão. O segundo argumento, se houver, deve ser do tipo integer; recebe o modificador de tipo associado ao tipo de destino, ou -1 se não houver nenhum. O terceiro argumento, se houver, deve ser do tipo boolean; recebe true se a conversão for explícita, ou false caso contrário (Estranhamente, a especificação SQL obriga comportamentos diferentes para conversões implícitas e explícitas em alguns casos. Este argumento é fornecido para funções que devem implementar estes tipos de conversão. Não é recomendado que se projete tipos de dado próprios de uma forma que isto tenha importância).
Normalmente, a conversão deve ter tipos de dado de origem e de destino diferentes. Entretanto, é permitido declarar uma conversão com tipos de dados de origem e de destino idênticos, se houver uma função de implementação da conversão com mais de um argumento. É utilizado para representar nos catálogos do sistema funções de modificação do comprimento específicas do tipo (type-specific length coercion functions). A função nomeada é utilizada para modificar (coerce) um valor do tipo para o valor do modificador do tipo indicado por seu segundo argumento (uma vez que atualmente a gramática permite que apenas certos tipos de dados nativos possuam modificadores de tipo, esta funcionalidade não tem utilidade para os tipos de destino definidos pelo usuário, mas de qualquer forma é mencionado para ficar completo).
Quando a conversão possui tipos de origem e de destino diferentes e uma função que recebe mais de um argumento, esta conversão representa a conversão de um tipo para o outro e a aplicação da modificação do comprimento em um único passo. Quando não existe disponível uma entrada deste tipo, a conversão para um tipo que usa modificador de tipo envolve duas etapas, uma para converter entre os tipos de dado e outra para aplicar o modificador.
Para remover conversões criadas pelo usuário deve ser utilizado o comando DROP CAST.
Lembre-se que para ser possível a conversão de tipos nas duas direções é necessário declarar conversões para as duas direções explicitamente.
Antes do PostgreSQL 7.3 toda função que possuía o mesmo nome de um tipo de dado, retornava este tipo de dado, e recebia um argumento de um tipo diferente era, automaticamente, uma função de conversão. Esta convenção foi abandonada devido à introdução dos esquemas e da capacidade de representar conversões binariamente compatíveis nos catálogos do sistema. As funções de conversão internas ainda seguem este esquema de nomes, mas também têm que aparecer como conversões no catálogo do sistema pg_cast.
Embora não seja requerido, é recomendado que se continue a seguir esta antiga convenção para dar nome às funções de implementação da conversão usando o nome do tipo de dado de destino. Muitos usuários estão acostumados a poderem converter tipos de dado usando a notação no estilo de função, ou seja, nome_do_tipo(x). Na verdade esta notação não é nada mais, nem menos, que uma chamada à função de implementação da conversão; não é tratada especialmente como uma conversão. Se os nomes de suas funções de conversão não aderirem a esta convenção então os usuários ficarão surpresos. Uma vez que o PostgreSQL permite a sobrecarga do mesmo nome de função com argumentos de tipos diferentes, não há dificuldade em ter-se várias funções de conversão de tipos diferentes todas usando o nome do tipo de destino.
Nota: Existe uma pequena mentira no parágrafo anterior: existe ainda um caso em que pg_cast é utilizada para resolver o significado de uma aparente chamada de função. Se a chamada de função nome(x) não corresponder a nenhuma função existente, mas nome for o nome de um tipo de dado, e pg_cast mostrar uma conversão binariamente compatível para o tipo de x, então a chamada será construída como uma conversão explícita. Esta exceção é feita para que as conversões compatíveis binariamente possam ser chamadas utilizando a sintaxe de função, embora não possuam nenhuma função.
Para criar uma conversão do tipo text para o tipo int4 utilizando a função int4(text):
CREATE CAST (text AS int4) WITH FUNCTION int4(text);
(Esta conversão já está pré-definida no sistema).