

# Gerenciar conversões personalizadas no RDS para PostgreSQL
<a name="PostgreSQL.CustomCasts"></a>

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](https://www.postgresql.org/docs/current/sql-createcast.html).

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\$1casts para instalar outras conversões para tipos integrados e, ao mesmo tempo, criar conversões próprias para tipos personalizados

**Topics**
+ [Instalar e usar a extensão rds\$1casts](#PostgreSQL.CustomCasts.Installing)
+ [Conversões compatíveis](#PostgreSQL.CustomCasts.Supported)
+ [Criar ou eliminar conversões](#PostgreSQL.CustomCasts.Creating)
+ [Criar conversões personalizadas com a estratégia de contexto adequada](#PostgreSQL.CustomCasts.BestPractices)

## Instalar e usar a extensão rds\$1casts
<a name="PostgreSQL.CustomCasts.Installing"></a>

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
<a name="PostgreSQL.CustomCasts.Supported"></a>

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\$1casts oferece dois tipos de função de conversão para cada conversão:
+ *\$1inout 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.
+ *\$1custom 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
<a name="PostgreSQL.CustomCasts.Creating"></a>

É possível criar e eliminar conversões compatíveis usando dois métodos:

### Criação de conversão
<a name="PostgreSQL.CustomCasts.Creating.Methods"></a>

**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\$1casts.create\$1cast**

```
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\$1cast:

```
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
<a name="PostgreSQL.CustomCasts.Dropping"></a>

**Método 1: usar o comando DROP CAST**

```
DROP CAST IF EXISTS (text AS numeric);
```

**Método 2: usar a função rds\$1casts.drop\$1cast**

```
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
<a name="PostgreSQL.CustomCasts.BestPractices"></a>

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.