Ao discutir uma solução CTE recursiva para esta pergunta:
O @ypercube encontrou uma exceção surpreendente, o que nos levou a investigar a manipulação de modificadores de tipo. Encontramos um comportamento surpreendente.
1. A conversão de tipo mantém o modificador de tipo em alguns contextos
Mesmo quando instruído a não fazê-lo. O exemplo mais básico:
SELECT 'vc8'::varchar(8)::varchar
Pode-se esperar varchar
(sem modificador), pelo menos eu esperaria. Mas o resultado é varchar(8)
(com modificador). Muitos casos relacionados no violino abaixo.
2. A concatenação da matriz perde o modificador de tipo em alguns contextos
Sem necessidade, então isso erra no lado oposto:
SELECT ARRAY['vc8']::varchar(8)[]
, ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
A primeira expressão produz varchar(8)[]
como esperado.
Mas o segundo, depois de concatenar outro, varchar(8)
é diluído em apenas varchar[]
(sem modificador). Comportamento semelhante de array_append()
, exemplos no violino abaixo.
Tudo isso não importa na maioria dos contextos. O Postgres não perde dados e, quando atribuído a uma coluna, o valor é coagido para o tipo certo de qualquer maneira. No entanto , errar em direções opostas culmina em uma exceção surpreendente:
3. O CTE recursivo exige que os tipos de dados correspondam exatamente
Dada esta tabela simplificada:
CREATE TABLE a (
vc8 varchar(8) -- with modifier
, vc varchar -- without
);
INSERT INTO a VALUES ('a', 'a'), ('bb', 'bb');
Embora este rCTE funcione para a varchar
coluna vc
, ele falha na varchar(8)
coluna vc8
:
WITH RECURSIVE cte AS (
(
SELECT ARRAY[vc8] AS arr -- produces varchar(8)[]
FROM a
ORDER BY vc8
LIMIT 1
)
UNION ALL
(
SELECT a.vc8 || c.arr -- produces varchar[] !!
FROM cte c
JOIN a ON a.vc8 > c.arr[1]
ORDER BY vc8
LIMIT 1
)
)
TABLE cte;
ERRO: a coluna 1 da consulta recursiva "cte" tem o caractere de tipo variando (8) [] em termos não recursivos, mas o caractere de tipo variando [] no geral Dica: transmita a saída do termo não recursivo para o tipo correto. Posição: 103
Uma solução rápida seria lançar para text
.
Uma UNION
consulta simples não apresenta o mesmo problema: ela se conforma com o tipo sem modificador, o que garante a preservação de todas as informações. Mas o rCTE é mais exigente.
Além disso, você não enfrentaria problemas com os mais usados em max(vc8)
vez de ORDER BY
/ LIMIT 1
, porque os max()
amigos se contentam text
imediatamente (ou com o respectivo tipo de base sem modificador).
SQL Fiddle demonstrando três coisas:
- Uma variedade de expressões de exemplo, incluindo resultados surpreendentes.
- Um rCTE simples que funciona com
varchar
(sem modificador). - O mesmo rCTE gerando uma exceção para
varchar(n)
(com modificador).
O violino é para a página 9.3. Eu obtenho os mesmos resultados localmente para a página 9.4.4.
Criei tabelas a partir das expressões demo para poder mostrar o tipo de dados exato, incluindo o modificador. Enquanto o pgAdmin mostra essas informações prontamente, elas não estão disponíveis no sqlfiddle. Notavelmente, também não está disponível em psql
(!). Isso é conhecido por falhas no psql e uma possível solução foi discutida em pgsql-hackers antes - mas ainda não foi implementada. Esse pode ser um dos motivos pelos quais o problema ainda não foi detectado e corrigido.
No nível SQL, você pode usar pg_typeof()
para obter o tipo (mas não o modificador).
Questões
Juntos, os três problemas fazem uma bagunça.
Para ser preciso, a edição 1. não está diretamente envolvida, mas arruina a correção aparentemente óbvia com um elenco no termo não recursivo: ARRAY[vc8]::varchar[]
ou similar, o que aumenta a confusão.
Qual desses itens é um bug, uma falha ou exatamente como deveria ser?
Estou faltando alguma coisa ou devemos relatar um bug?
UNION
consultas simples . Será que encontramos três pequenos insetos independentes ao mesmo tempo? (Depois de meses e meses sem essa descoberta.) Qual deles você acha que deve ser arquivado como bug?Respostas:
Isso ocorre devido aos atributos de relação (definidos em
pg_class
epg_attribute
, ou definidos dinamicamente a partir de umaselect
instrução) que suportam modificadores (viapg_attribute.atttypmod
), enquanto os parâmetros de função não. Os modificadores são perdidos quando processados por meio de funções e, como todos os operadores são manipulados por meio de funções, os modificadores também são perdidos quando processados por operadores.Funções com valores de saída, ou que retornam conjuntos de registros ou o equivalente,
returns table(...)
também não conseguem reter quaisquer modificadores incluídos na definição. No entanto, tabelas quereturn setof <type>
reterão (na verdade, provavelmente serão projetadas para) quaisquer modificadores definidos paratype
inpg_attribute
.fonte