
Na série "All Your GUCs in a Row", publicada no Planet PostgreSQL↳PostgreSQL11 conteúdosPostgreSQL via SSL com GolangData · abr 20195 itens legais sobre data types do PostgreSQLData · mar 20195 serviços gratuitos na cloud para bancos de dados PostgresData · fev 2025Ver tudo em Data →, um post dedicado ao lo_compat_privileges merece atenção de qualquer DBA que administre bases em produção. O parâmetro é descrito ali como o membro "com a pior personalidade" da família de flags de compatibilidade do PostgreSQL, e a razão é direta: seu efeito colateral é abrir toda a segurança de large objects sem deixar rastro.
O que o parâmetro faz
Até o PostgreSQL 8.4, um large object não pertencia a ninguém: qualquer role capaz de conectar ao banco podia lê-lo, sobrescrevê-lo ou removê-lo. A partir do 9.0, cada large object passou a ter dono e uma ACL, registrados em pg_largeobject_metadata. As regras são as que se espera de uma tabela: SELECT para ler, UPDATE para escrever ou truncar, e propriedade (ownership) para executar lo_unlink(), comentar, conceder privilégios ou transferir o objeto.
O lo_compat_privileges é o botão que finge que o 9.0 nunca chegou. Com ele ligado (on), as verificações de leitura e escrita em lo_open() são ignoradas, e com elas tudo que se apoia nessa função: lo_get(), lo_put(), lo_read(), lo_truncate() e o lo_export() do lado cliente. Também some a checagem de ownership em lo_unlink() e a que protege COMMENT ON LARGE OBJECT e SECURITY LABEL. Segundo o texto, essa lista foi conferida contra o código-fonte do PostgreSQL 18 e depois testada em um cluster descartável, justamente porque a documentação descreve o limite "apenas de forma vaga".
Nem tudo cai. GRANT ... ON LARGE OBJECT continua exigindo grant options, ALTER LARGE OBJECT ... OWNER TO ainda exige ser o dono (ou membro da role dona), e as versões server-side de lo_import() e lo_export() seguem precisando de EXECUTE, revogado de PUBLIC desde o PostgreSQL 11, pela boa razão de que essas funções leem e escrevem no sistema de arquivos do servidor com o usuário de SO do próprio Postgres.
Por que ele é perigoso na prática
O padrão é off, e o contexto é superuser, ou seja, não há restart envolvido. Um superusuário pode aplicá-lo em sessão, fixá-lo em uma role ou banco com ALTER ROLE ... SET / ALTER DATABASE ... SET, ou, a partir do PostgreSQL 15, delegá-lo via GRANT SET ON PARAMETER lo_compat_privileges. Cada um desses caminhos é uma forma de rebaixar a segurança de um banco inteiro a partir de um lugar onde ninguém vai pensar em olhar.
É aí que o post crava a diferença em relação aos outros GUCs de compatibilidade, como array_nulls, backslash_quote e escape_string_warning. Esses preservam uma sintaxe ou um formato. O lo_compat_privileges desativa um modelo de permissões para todos os large objects do banco, e faz isso silenciosamente. Não há linha de log, não há warning, não há nada em qualquer view de estatística indicando que os large objects estão, naquele momento, abertos para escrita por qualquer um. A única evidência é rodar SHOW lo_compat_privileges, e quem roda isso já desconfia do problema.
Por que alguém liga isso em 2026
Ninguém ativa o parâmetro por compatibilidade real com o 8.4. As razões concretas, aponta o post, são mais constrangedoras. A mais comum: os large objects foram importados por uma role (um usuário de migração, um superusuário rodando \lo_import, um job de ETL) e a aplicação conecta com outra role. O primeiro lo_get() falha com permission denied for large object, e alguém descobre que esse parâmetro faz o erro sumir.
A outra: o vacuumlo chama lo_unlink() em cada órfão que encontra. Se ele conecta com uma role que não é dona dos objetos, a primeira remoção falha com must be owner of large object, o que deixa a transação em batch em estado de erro e encerra a execução.
As correções corretas são baratas
O ponto central do artigo é que ambos os casos têm solução simples, sem precisar comprometer a segurança do banco. Para o vacuumlo, basta rodá-lo como o dono dos objetos ou como superusuário.
Para o descompasso de ownership, a própria pg_largeobject_metadata diz exatamente o que pertence a quem, e o \gexec do psql resolve o resto:
SELECT format('ALTER LARGE OBJECT %s OWNER TO app;', oid)
FROM pg_largeobject_metadata
WHERE lomowner <> 'app'::regrole \gexecSe a aplicação só precisa ler os objetos, a versão mais restrita é trocar por GRANT SELECT ON LARGE OBJECT no mesmo laço, concedendo apenas o privilégio necessário em vez de transferir a propriedade. O texto lembra, em tom de ressalva, que a correção de fundo é parar de guardar arquivos como large objects, mas isso é assunto para outro post.
A lição para quem opera em produção
A recomendação é direta: deixe o parâmetro desligado. Se você herdar um banco onde ele está on, desligue-o em sessão, encontre a query que quebra e conserte o objeto daquela query com um GRANT ou um ALTER ... OWNER. A frase que encerra o texto sintetiza a filosofia de quem trata dado como ativo crítico: "O erro era o relatório de bug. O parâmetro era alguém se recusando a lê-lo."
O recado prático vale para o dia a dia de qualquer DBA no Brasil: um permission denied não é um obstáculo a ser silenciado com um flag global, é um sinal de que o modelo de propriedade e privilégios está desalinhado com o desenho da aplicação. Corrigir a origem custa poucas linhas de SQL e mantém a integridade que o PostgreSQL passou a oferecer desde a versão 9.0. Silenciar o erro com lo_compat_privileges deixa a base inteira exposta, sem log e sem aviso, esperando uma auditoria descobrir.
Fonte: Planet PostgreSQL
Este artigo foi escrito por Roberto Diniz, colunista de banco de dados do iMasters, um agente de inteligência artificial com revisão editorial humana. Publicado sob revisão editorial de Rafael Chinaglia - iMasters. Saiba como produzimos no expediente.










Comentários
Ninguém comentou ainda. Começa a conversa?