Gerenciar conversões personalizadas no RDS para PostgreSQL
No PostgreSQL, a conversão de tipo refere-se ao processo de conversão de um valor de um tipo de dados em outro. O PostgreSQL oferece conversões integradas para várias conversões comuns, mas também é possível criar conversões personalizadas para definir como as conversões de tipos específicos devem se comportar.
Um conversão especifica como realizar uma conversão de um tipo de dados em outro. Por exemplo, converter texto '123' em número inteiro 123 ou numérico 45.67 em texto '45.67'.
Para ter informações abrangentes sobre os conceitos e a sintaxe de conversão do PostgreSQL, consulte CREATE CAST na documentação do PostgreSQL
A partir das versões do RDS para PostgreSQL 13.23, 14.20, 15.15, 16.11, 17.7 e 18.1, é possível usar a extensão rds_casts para instalar outras conversões para tipos integrados e, ao mesmo tempo, criar conversões próprias para tipos personalizados
Tópicos
Instalar e usar a extensão rds_casts
Para criar a extensão rds_casts, conecte-se à sua instância de banco de dados do RDS para PostgreSQL como um rds_superuser e execute o seguinte comando:
CREATE EXTENSION IF NOT EXISTS rds_casts;
Conversões compatíveis
Crie a extensão em cada banco de dados em que você deseja usar conversões personalizadas. Depois de criar a extensão, use o seguinte comando para visualizar todas as conversões disponíveis:
SELECT * FROM rds_casts.list_supported_casts();
Essa função lista as combinações de conversão disponíveis (tipo de origem, tipo de destino, contexto de coerção e função de conversão). Por exemplo, se você quiser criar uma conversão de text em numeric como uma conversão implicit. É possível usar a seguinte consulta para descobrir se a conversão está disponível para criação:
SELECT * FROM rds_casts.list_supported_casts() WHERE source_type = 'text' AND target_type = 'numeric'; id | source_type | target_type | qualified_function | coercion_context ----+-------------+-------------+--------------------------------------+------------------ 10 | text | numeric | rds_casts.rds_text_to_numeric_custom | implicit 11 | text | numeric | rds_casts.rds_text_to_numeric_custom | assignment 13 | text | numeric | rds_casts.rds_text_to_numeric_custom | explicit 20 | text | numeric | rds_casts.rds_text_to_numeric_inout | implicit 21 | text | numeric | rds_casts.rds_text_to_numeric_inout | assignment 23 | text | numeric | rds_casts.rds_text_to_numeric_inout | explicit
A extensão rds_casts oferece dois tipos de função de conversão para cada conversão:
-
_inout functions: usam o mecanismo de conversão de E/S padrão do PostgreSQL, comportando-se de forma idêntica às conversões criadas com o método INOUT.
-
_custom functions: oferecem lógica de conversão aprimorada que lida com casos extremos, como converter strings vazias em valores NULL para evitar erros de conversão
As funções inout replicam o comportamento de conversão nativo do PostgreSQL, enquanto as funções custom estendem essa funcionalidade, lidando com cenários que as conversões INOUT padrão não conseguem atender, como converter strings vazias em números inteiros.
Criar ou eliminar conversões
É possível criar e eliminar conversões compatíveis usando dois métodos:
Criação de conversão
Método 1: usar o comando nativo CREATE CAST
CREATE CAST (text AS numeric) WITH FUNCTION rds_casts.rds_text_to_numeric_custom AS IMPLICIT;
Método 2: usar a função rds_casts.create_cast
SELECT rds_casts.create_cast(10);
A função create_cast obtém o ID da saída list_supported_casts(). Esse método é mais simples e garante o uso da combinação correta de função e contexto. Esse ID com certeza permanecerá o mesmo em diferentes versões do Postgres.
Para verificar se a conversão foi criada com êxito, consulte o catálogo de sistema pg_cast:
SELECT oid, castsource::regtype, casttarget::regtype, castfunc::regproc, castcontext, castmethod FROM pg_cast WHERE castsource = 'text'::regtype AND casttarget = 'numeric'::regtype; oid | castsource | casttarget | castfunc | castcontext | castmethod --------+------------+------------+--------------------------------------+-------------+------------ 356372 | text | numeric | rds_casts.rds_text_to_numeric_custom | i | f
A coluna castcontext mostra: e para EXPLICIT, a para ASSIGNMENT ou i para IMPLICIT.
Eliminar conversões
Método 1: usar o comando DROP CAST
DROP CAST IF EXISTS (text AS numeric);
Método 2: usar a função rds_casts.drop_cast
SELECT rds_casts.drop_cast(10);
A função drop_cast usa o mesmo ID usado ao criar a conversão. Esse método garante a eliminação da conversão exata que foi criada com o ID correspondente.
Criar conversões personalizadas com a estratégia de contexto adequada
Ao criar várias conversões para tipos inteiros, erros de ambiguidade de operador poderão ocorrer se todas as conversões forem criadas como IMPLICIT. O seguinte exemplo demonstra esse problema criando duas conversões implícitas de texto em números inteiros com largura diferente:
-- Creating multiple IMPLICIT casts causes ambiguity postgres=> CREATE CAST (text AS int4) WITH FUNCTION rds_casts.rds_text_to_int4_custom(text) AS IMPLICIT; CREATE CAST postgres=> CREATE CAST (text AS int8) WITH FUNCTION rds_casts.rds_text_to_int8_custom(text) AS IMPLICIT; CREATE CAST postgres=> CREATE TABLE test_cast(col int); CREATE TABLE postgres=> INSERT INTO test_cast VALUES ('123'::text); INSERT 0 1 postgres=> SELECT * FROM test_cast WHERE col='123'::text; ERROR: operator is not unique: integer = text LINE 1: SELECT * FROM test_cast WHERE col='123'::text; ^ HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
O erro ocorre porque o PostgreSQL não consegue determinar qual conversão implícita usar ao comparar uma coluna de inteiros com um valor de texto. As conversões implícitas int4 e int8 são ambas candidatas válidas, o que cria ambiguidade.
Para evitar essa ambiguidade de operador, use o contexto ASSIGNMENT para inteiros com largura menor e o contexto IMPLICIT para inteiros com largura maior:
-- Use ASSIGNMENT for smaller integer widths CREATE CAST (text AS int2) WITH FUNCTION rds_casts.rds_text_to_int2_custom(text) AS ASSIGNMENT; CREATE CAST (text AS int4) WITH FUNCTION rds_casts.rds_text_to_int4_custom(text) AS ASSIGNMENT; -- Use IMPLICIT for larger integer widths CREATE CAST (text AS int8) WITH FUNCTION rds_casts.rds_text_to_int8_custom(text) AS IMPLICIT; postgres=> INSERT INTO test_cast VALUES ('123'::text); INSERT 0 1 postgres=> SELECT * FROM test_cast WHERE col='123'::text; col ----- 123 (1 row)
Com essa estratégia, somente a conversão int8 é implícita, possibilitando que o PostgreSQL determine de forma inequívoca qual conversão usar.