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?