Contents
pg_try_cast
TRY_CAST e TRY_CONVERT para PostgreSQL. Converte um valor para qualquer tipo e devolve NULL
(ou um valor padrão que você escolher) quando ele não é válido. A query continua rodando, em vez de abortar com erro.
SELECT try_cast('42', NULL::int); -- 42
SELECT try_cast('abc', NULL::int); -- NULL (sem erro!)
SELECT try_cast('abc', 0); -- 0 (valor padrão)
Por quê?
No PostgreSQL, basta uma linha ruim para derrubar a query inteira:
SELECT id, preco::numeric FROM importacao;
-- ERROR: invalid input syntax for type numeric: "R$ 10,00"
O SQL Server resolve isso com TRY_CAST e TRY_CONVERT, e o Oracle com CAST ... DEFAULT ... ON CONVERSION ERROR.
O PostgreSQL ainda não tem nenhum dos dois. As gambiarras de sempre são uma regex antes de cada cast (frágil, e diferente
para cada tipo) ou uma função PL/pgSQL com bloco EXCEPTION, que abre uma subtransação a cada linha e é lenta.
O pg_try_cast é uma extensão pequena em C que faz isso do jeito certo. Funciona com qualquer tipo que o PostgreSQL
saiba ler a partir de texto: inteiros, numéricos, datas, timestamps, uuid, json/jsonb, arrays, ranges, enums, domínios
e tipos compostos. E é cerca de 16× mais rápida que a solução em PL/pgSQL.
Como usar
try_cast(valor text, padrao anyelement) → anyelement
O tipo de padrao define para qual tipo valor será convertido. O valor de padrao é o que volta quando a
conversão falha.
SELECT try_cast('2026-10-17', NULL::date); -- 2026-10-17
SELECT try_cast('2026-02-30', NULL::date); -- NULL
SELECT try_cast('99999999999', NULL::int); -- NULL (fora do intervalo)
SELECT try_cast('n/d', 0); -- 0
SELECT try_cast('{"a":', '{}'::jsonb); -- {}
SELECT try_cast(NULL, 0); -- NULL (entra NULL, sai NULL)
try_convert(padrao anyelement, valor text) → anyelement
É a mesma função, com a ordem de argumentos do SQL Server, para facilitar migrações:
SELECT try_convert(NULL::int, '42'); -- 42
SELECT try_convert(NULL::int, 'abc'); -- NULL
Exemplo real: limpando uma importação
SELECT id,
try_cast(valor_bruto, NULL::numeric(12,2)) AS valor,
try_cast(data_bruta, NULL::date) AS pago_em,
try_cast(email_bruto, NULL::varchar(120)) AS email
FROM importacao;
-- encontrar as linhas com problema
SELECT * FROM importacao
WHERE valor_bruto IS NOT NULL
AND try_cast(valor_bruto, NULL::numeric) IS NULL;
Colinha SQL Server → PostgreSQL
| SQL Server | pg_try_cast |
|---|---|
TRY_CAST(x AS int) |
try_cast(x, NULL::int) |
TRY_CAST(x AS decimal(10,2)) |
try_cast(x, NULL::numeric(10,2)) |
TRY_CONVERT(date, x) |
try_convert(NULL::date, x) |
ISNULL(TRY_CAST(x AS int), 0) |
try_cast(x, 0) |
TRY_CAST(x AS uniqueidentifier) |
try_cast(x, NULL::uuid) |
Instalação
Requer PostgreSQL 16 ou mais novo, porque usa as funções de entrada com “erro suave” (soft error) que surgiram no PostgreSQL 16.
Linux (Debian / Ubuntu)
sudo apt install build-essential postgresql-server-dev-17 # use a versão do seu PostgreSQL
git clone https://github.com/SEU-USUARIO/pg_try_cast.git
cd pg_try_cast
make
sudo make install
Linux (RHEL / Rocky / Fedora)
sudo dnf install gcc make postgresql17-devel # o nome do pacote pode variar conforme o repositório
make PG_CONFIG=/usr/pgsql-17/bin/pg_config
sudo make install PG_CONFIG=/usr/pgsql-17/bin/pg_config
macOS (Homebrew)
brew install postgresql@17
make PG_CONFIG=$(brew --prefix postgresql@17)/bin/pg_config
make install PG_CONFIG=$(brew --prefix postgresql@17)/bin/pg_config
Docker (qualquer sistema, inclusive Windows)
docker build -t pg_try_cast . # PostgreSQL 17 por padrão
docker build --build-arg PG_MAJOR=18 -t pg_try_cast:18 .
docker run -d --name pgtry -e POSTGRES_PASSWORD=postgres -p 5432:5432 pg_try_cast
Erro de compilação falando de
clangoullvm? Alguns pacotes do PostgreSQL foram compilados com suporte a JIT e tentam gerar um “bitcode” LLVM extra, que você não precisa. Compile commake with_llvm=no && sudo make with_llvm=no install.
Ativando no banco
CREATE EXTENSION pg_try_cast;
A extensão é marcada como trusted. Qualquer usuário com privilégio CREATE no banco pode ativá-la, sem precisar de
superusuário.
Como ela se comporta
- Entra NULL, sai NULL.
try_cast(NULL, 0)devolveNULL, igual aCAST(NULL AS int). - A entrada é texto. Para outros tipos, use
::text. Exemplo:try_cast(minha_coluna::text, NULL::int). - Modificadores de tipo são respeitados, com as mesmas regras de um
INSERTouCOPYnuma coluna daquele tipo:try_cast('abcd', NULL::varchar(3))devolveNULL, porque o valor não cabe. UmCASTcomum truncaria para'abc'.try_cast('123.456', NULL::numeric(5,2))devolve123.46, arredondado como onumeric(5,2)faz.try_cast('12345.6', NULL::numeric(5,2))devolveNULL(estouro).
- Os
CHECKde domínios são verificados. ComCREATE DOMAIN positivo AS int CHECK (VALUE > 0),try_cast('-5', NULL::positivo)devolveNULL. - O tipo do padrão precisa ser conhecido.
try_cast('1', NULL)dá erro. EscrevaNULL::int.
Limitações
- Algumas funções de entrada não suportam erro suave e continuam lançando erro:
regtypecom um nome sintaticamente inválido (por exemplo'###'). Nomes válidos que não existem devolvemNULL.- Tipos de outras extensões cujos autores ainda não adotaram a API de erro suave.
- Só converte a partir de texto, que é o formato em que dados sujos costumam chegar. Casts como
numeric → intcom arredondamento são outra operação e não fazem parte desta extensão.
Desempenho
1.000.000 de linhas, 10% inválidas, PostgreSQL 16, sem workers paralelos, mediana de 5 execuções:
| Método | Tempo |
|---|---|
try_cast(v, NULL::int) |
~110 ms |
CASE WHEN pg_input_is_valid(v, 'int4') THEN v::int END |
~117 ms |
Função PL/pgSQL com BEGIN ... EXCEPTION ... END |
~1.790 ms |
O try_cast é tão rápido quanto a checagem nativa pg_input_is_valid(), porque os dois usam o mesmo mecanismo.
A vantagem é que uma única chamada faz a checagem e a conversão, aceita valor padrão e respeita o modificador do tipo
de destino. Rode você mesmo com psql -f bench/benchmark.sql.
O PostgreSQL já não tem isso?
Ainda não. O PostgreSQL 16 trouxe pg_input_is_valid() e pg_input_error_info(), que dizem se um valor é válido,
mas não convertem. A sintaxe do padrão SQL, CAST(x AS int DEFAULT 0 ON CONVERSION ERROR), está em discussão na lista
pgsql-hackers, mas não entrou no PostgreSQL 19 (até o RC 1). O pg_try_cast funciona hoje, a partir do PostgreSQL 16.
Rodando os testes
make
sudo make install
make installcheck # precisa de um servidor rodando ao qual você consiga conectar
Documentação extra
Contribuindo
Issues e pull requests são bem-vindos. Qualquer mudança de comportamento deve vir com um teste de regressão em
test/sql/ e a saída esperada em test/expected/.
Licença
Distribuída sob a Licença PostgreSQL.