Resultados surpreendentes para tipos de dados com modificador de tipo

11

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 varcharcoluna 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 UNIONconsulta 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 textimediatamente (ou com o respectivo tipo de base sem modificador).

SQL Fiddle demonstrando três coisas:

  1. Uma variedade de expressões de exemplo, incluindo resultados surpreendentes.
  2. Um rCTE simples que funciona com varchar(sem modificador).
  3. 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?

Erwin Brandstetter
fonte
Isso certamente parece bastante suspeito. Suspeito que a compatibilidade com versões anteriores de consultas de união existentes possa desempenhar um papel.
Craig Ringer
@ CraigRinger: Não vejo por que a concatenação do array descarta o modificador sem necessidade e o elenco não, mesmo que solicitado. Nem por que o rCTE precisa ser mais rigoroso (menos inteligente) que as UNIONconsultas 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?
Erwin Brandstetter

Respostas:

1

Isso ocorre devido aos atributos de relação (definidos em pg_classe pg_attribute, ou definidos dinamicamente a partir de uma selectinstrução) que suportam modificadores (via pg_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 que return setof <type>reterão (na verdade, provavelmente serão projetadas para) quaisquer modificadores definidos para typein pg_attribute.

Ziggy Crueltyfree Zeitgeister
fonte