Executar uma operação matemática em um real
operando e um numeric
operando no PostgreSQL resulta em um double precision
valor, mas acho isso surpreendente. Por que o resultado não seria um real
, em vez disso?
Confirmei esse comportamento com o PostgreSQL 13 e 16.
SELECT
pg_typeof(0::numeric + 0::numeric), -- numeric
pg_typeof(0::real + 0::real), -- real
pg_typeof(0::numeric + 0::real), -- double precision??
pg_typeof(0::real + 0::numeric); -- double precision??
Li partes substanciais da documentação do PostgreSQL, mais relevantes: 10.2. Conversão de tipo: operadores
E eu consultei pg_catalog.pg_cast
(ou psql
's \dC
) para entender quais são as conversões implícitas relevantes:
> SET search_path = 'pg_catalog';
> SELECT format_type(castsource, NULL) AS source,
format_type(casttarget, NULL) AS target
FROM pg_cast
WHERE castcontext = 'i' -- i.e. casts allowed implicitly
AND ARRAY[format_type(castsource, NULL), format_type(casttarget, NULL)]
<@ '{numeric,real,double precision}'::text[];
O resultado é:
source | target
---------+------------------
real | double precision
numeric | real
numeric | double precision
numeric | numeric
(4 rows)
Então, pelo que entendi, para 0::numeric + 0::real
o PostgreSQL deveria (os trechos são todos de 10.2. Conversão de Tipo: Operadores ):
- Selecione os operadores a serem considerados do
pg_operator
catálogo do sistema. Se um nome de operador não qualificado por esquema foi usado (o caso usual), os operadores considerados são aqueles com o nome correspondente e a contagem de argumentos que são visíveis no caminho de pesquisa atual.
Espero que comece com todos +
os operadores - cada um aceita um par dos mesmos tipos numéricos ( real + real
, integer + integer
, etc.)
- Verifique se há um operador que aceita exatamente os tipos de argumentos de entrada.
Na etapa 2, nenhum operador é uma correspondência exata para real
e numeric
, então espero que nada aconteça aqui.
3a. Descarte operadores candidatos cujos tipos de entrada não correspondem e não podem ser convertidos (usando uma conversão implícita) para corresponder.
Espero que isso descarte a maioria dos candidatos. Ele deve manter exatamente dois: o de real
, e o de double precision
que ele aparentemente acaba usando. (Note que real
não pode ser implicitamente convertido para numeric
, então ele não deve manter o de numeric
.)
3b. Se qualquer argumento de entrada for de um tipo de domínio, trate-o como sendo do tipo base do domínio para todas as etapas subsequentes.
Espero que isso não faça nada, pois não há tipos de domínio aqui.
3c. Execute todos os candidatos e mantenha aqueles com as correspondências mais exatas em tipos de entrada. Mantenha todos os candidatos se nenhum tiver correspondências exatas. Se apenas um candidato permanecer, use-o; caso contrário, continue para a próxima etapa.
Aqui, espero que ele descarte o operador for double precision
, porque o operador for real
tem uma correspondência exata, enquanto o for double precision
tem zero correspondências exatas. E então, como só resta um candidato aqui, ele deve usar o operador for real
, e espero que o resultado seja um real
. Mas, em vez disso, o resultado é um double precision
valor.
Então, minha pergunta é por que o PostgreSQL está escolhendo o double precision
operador neste caso, onde ele precisa converter implicitamente ambos os operandos em vez de apenas um deles? A documentação está errada, ou meu entendimento dela está errado?