Temos visto repetidamente tentativas fracassadas de indexar colunas com valores que excedem um tamanho máximo. O Postgres 10 tem esse tipo de mensagem de erro:
ERROR: index row size xxxx exceeds maximum 2712 for index "foo_idx" HINT: Values larger than 1/3 of a buffer page cannot be indexed. Consider a function index of an MD5 hash of the value, or use full text indexing.
Exemplos:
- Sobrecarga de índice variável de caracteres e limite de comprimento
- Erro de tamanho máximo da linha do índice
etc.
Agora, a_horse_with_no_name demonstrou um caso com valores muito maiores text
(10.000 caracteres) que ainda parece funcionar com um UNIQUE
índice no Postgres 9.6. Citando seu caso de teste:
create table tbl (col text);
create unique index on tbl (col);
insert into tbl
values (rpad(md5(random()::text), 10000, md5(random()::text)));
select length(val) from x; -- 10000
Nenhum erro e o valor da coluna realmente testado com um comprimento de 10.000 caracteres.
Houve mudanças recentes ou como isso é possível?
Resposta curta: compressão.
O tipo de dados
text
permite compactação (sem perdas!) e armazenamento fora de linha por padrão:O manual sobre
pg_type.typstorage
:Teste com
pg_column_size()
em vez delength()
. Certifique-se de testar as colunas reais da tabela (com compactação aplicada) e não apenas os valores de entrada. Ver:db<>fique aqui
Observe como o valor é forçado a ser descompactado de seu formato de armazenamento com a expressão noop:
pg_column_size(col || '')
.A 5ª linha seria muito grande para caber na tupla do índice (mesmo com compactação) e acionaria a mensagem de erro no título.
A 6ª linha seria grande demais para caber até mesmo na página de índice e acionar a mensagem de erro relacionada:
Os valores de teste gerados com
rpad()
padrões de repetição, que permitem compactação massiva. Mesmo cordas muito longas ainda se encaixam facilmente no max. tamanho após a compressão desta forma.Relacionado:
Resposta longa
Executei testes mais extensos, adulterando os internos de armazenamento para verificar meu entendimento. Apenas para fins de teste!
dbfiddle não permite acesso de gravação aos catálogos do sistema. Mas as consultas estão aí para tentar "em casa".