
Toda equipe que adota uma ferramenta de migração cedo ou tarde vai atrás de uma flag. O release precisa de algo que a ferramenta não previu: uma checagem que roda depois da mudança de schema mas antes do commit, um CREATE INDEX CONCURRENTLY no meio de um deploy transacional, uma regra de ordenação que nome de arquivo não expressa. Começa então a peregrinação pela referência de configuração, pelo issue tracker e pelo changelog de uma versão que ainda não saiu.
É desse sintoma que parte o artigo "Your Deployment Is a 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 → Program", de Alexey Evlampiev, publicado no Planet PostgreSQL. A tese é direta: toda ferramenta de migração é, no fundo, um programa que executa o seu SQL. Logo, toda semântica de deploy (o que roda, em que ordem, dentro de qual transação, e se o resultado pode ou não fazer commit) pertence ao vocabulário da ferramenta. Qualquer coisa fora desse vocabulário vira feature request.
O que uma ferramenta externa é obrigada a assumir
Evlampiev não acusa as ferramentas de mal construídas, e o texto é explícito nisso. O argumento é estrutural: por rodar como processo separado, fora do banco, a ferramenta é forçada a assumir três responsabilidades.
A primeira é reconhecer os limites de cada instrução. O protocolo simple-query do PostgreSQL aceita uma string com várias instruções, então cruzar a conexão não exige, por si, um split no cliente. O problema aparece um passo adiante: qualquer ferramenta que execute instruções separadamente, classifique-as ou varie o tratamento transacional precisa saber onde cada uma termina. Corpos dollar-quoted de funções e blocos DO, ponto e vírgula dentro de literais e comentários, e rotinas escritas com BEGIN ATOMIC transformam isso num problema léxico. O scanner que resolve isso vira uma segunda superfície de compatibilidade entre o SQL que o projeto escreveu e o SQL que o PostgreSQL de fato recebe.
A segunda é decidir o contexto transacional antes do SQL da migração rodar. O arquivo pode conter BEGIN e COMMIT, mas não pode escolher o contexto em que é colocado, porque a ferramenta já abriu (ou recusou) uma transação em volta dele. É por isso que o Flyway expõe executeInTransaction=false para casos como o CREATE INDEX CONCURRENTLY. A configuração é correta, mas revela algo: a fronteira transacional virou metadado sobre o seu SQL, em vez de uma instrução dentro do programa de deploy.
A terceira é manter um modelo do que já rodou. A history table é um registro durável do que a ferramenta acredita ter aplicado. Como é mantida à parte do estado real do banco, os dois podem divergir, e daí a necessidade de caminhos de repair, baseline ou reconciliação nas ferramentas maduras.
A inversão: o projeto passa a dominar o loop
A proposta muda uma coisa só. Em vez de a ferramenta abrir a conexão e mandar as instruções segundo o próprio modelo, ela prepara uma sessão PostgreSQL, materializa o projeto como relações dentro dela e executa um único arquivo que o projeto possui. A ferramenta guarda o mecanismo de execução; o projeto assume a política.
O destino cabe numa tela, e o autor o mostra antes do argumento:
-- deploy.sql — o deploy, como um programa
BEGIN;
DO $$
DECLARE v_file record;
BEGIN
FOR v_file IN
SELECT path, content
FROM pg_temp.pgmi_plan_view
ORDER BY execution_order
LOOP
EXECUTE v_file.content;
END LOOP;
END $$;
CALL pgmi_test(); -- cada teste em seu próprio savepoint
COMMIT; -- só chega aqui se todos os testes passaramNenhuma configuração externa decidiu esse fluxo. O loop define a ordem de execução porque o projeto escreveu o loop; o commit é condicionado porque o projeto colocou a chamada de teste acima dele. São instruções SQL comuns, revisadas e alteradas como SQL comum. É o que faz a ferramenta descrita, o pgmi, ser essencialmente inversão de controle aplicada a deploy de banco: quem passa a dominar o control flow é o programa do projeto, não a ferramenta.
O handover é uma pequena API de sessão
O centro do desenho é o que a ferramenta entrega antes de o deploy.sql rodar, e essa superfície pública é pequena o bastante para caber na cabeça.
Os arquivos chegam como linhas. Todos os arquivos do projeto, não só os .sql, são carregados numa tabela temporária de escopo de sessão e expostos numa view. Cada linha traz caminho, conteúdo como texto, diretório, extensão, tamanho e dois checksums. Um project.json, um CSV de dados de referência ou um YAML de política também chegam como linhas, legíveis com content::jsonb na mesma query. Os dois checksums respondem a perguntas diferentes: um verifica se os bytes são iguais; o outro, sob conteúdo normalizado (comentários removidos, caixa unificada, espaços colapsados), verifica se mudou algo significativo. Qual dos dois é a identidade do arquivo é decisão de política, então ambos estão disponíveis.
Os parâmetros chegam duas vezes: como linhas em pgmi_parameter_view e como session settings, acessíveis com current_setting('pgmi.env', true). A segunda forma importa mais do que parece, porque um parâmetro fica legível de dentro de qualquer função chamada no deploy, em qualquer profundidade, sem ser passado como argumento.
E o plano é uma view, não uma lista. O pgmi_plan_view é derivado juntando a tabela de fontes com metadados parseados. Por ser uma relação e não um relatório, três propriedades seguem. É consultável: dá para o projeto fazer asserções sobre o próprio plano antes de executá-lo (por exemplo, garantir que nada precede a migração de tenancy, ou que duas migrações não reivindicam a mesma sort key) com um EXCEPT ou EXISTS na mesma transação. É derivado: um arquivo idempotente pode contribuir com várias linhas de execução, rodando cedo para criar roles e de novo depois para dar grant em objetos novos. E sua ordem não depende do locale do servidor, porque a ordenação usa COLLATE "C" (ordem de bytes), o que remove drift de plano entre o laptop do dev e a produção.
Os testes vivem numa árvore separada, sob __test__/, e CALL pgmi_test() se expande, antes do SQL chegar ao PostgreSQL, em SQL inline que percorre essa árvore com isolamento por savepoint em cada teste.
Onde o handover não é limpo
O autor faz questão de nomear os dois pontos em que a ferramenta ainda reescreve SQL do projeto, em vez de escondê-los. O primeiro é o macro CALL pgmi_test(), expandido em Go↳Go23 conteúdosEntendendo o Green Tea GC do Go 1.26Dev (Back & Front) · mai 2026Função recursiva em Go para acessar valores em mapas aninhadosDev (Back & Front) · set 2025Publicando projeto desenvolvido em Golang em um server grátisDev (Back & Front) · mar 2025Ver tudo em Dev (Back & Front) →, cuja expansão contém SAVEPOINT; como o PostgreSQL não permite savepoints no bloco transacional implícito de uma multi-statement query, o SQL gerado exige um BEGIN ... COMMIT explícito em volta.
O segundo é uma classificação léxica: o pgmi localiza o primeiro terminador de transação de nível superior e manda tudo até ali como uma unidade (a cabeça atômica, onde mora o gate de teste). O que vem depois é enviado instrução por instrução, sob o autocommit normal do PostgreSQL, o mesmo modelo do psql. É isso que torna possível um CREATE INDEX CONCURRENTLY sem segunda invocação da ferramenta nem setting por arquivo. A consequência prática é real e vem com aviso: instruções após um COMMIT no meio do arquivo não são agrupadas, e uma falha ali deixa aplicadas as instruções autocommitadas anteriores. O trabalho da cauda precisa ser seguro para reiniciar após sucesso parcial, e um CREATE INDEX CONCURRENTLY falho pode deixar um índice inválido para trás, que tem de ser tratado explicitamente.
Quando não vale a pena
A parte mais honesta do artigo é a que enumera os custos, e ela deveria pesar na decisão de qualquer DBA.
- Você escreve a orquestração que antes herdava. Os defaults de uma ferramenta de migração representam anos de decisões acumuladas sobre ordem, tratamento de falha e idempotência. No modelo invertido, essas decisões passam a ser suas, em PL/pgSQL. Equipe sem fluência em PL/pgSQL não deveria escolher isto.
- Para o caso simples, é pior. Se o deploy é uma sequência linear de arquivos numerados aplicados em ordem, o modelo da ferramenta encaixa direto no problema. O próprio Evlampiev recomenda o Flyway aí, com curva mais rasa e ecossistema maior. A regra de decisão: o modelo invertido só paga o custo quando o deploy tem forma (fases, condições, gates, ramificação dependente de dados).
- O ledger durável some por padrão. A history table de uma ferramenta externa sobrevive à sessão e ao operador. Aqui, o histórico de um ambiente é tão bom quanto o programa que o mantém, e quem não escreve rastreamento não tem nenhum. Três linhas no starter recuperam a semântica apply-once, mas a escolha agora é da equipe.
- Uma sessão é o modelo, e transaction pooling quebra isso. O handover vive em objetos session-local. Um pooler em modo de transação não prende o cliente ao mesmo backend, então o
pg_tempsome. Deploys precisam de conexão direta ou session pooling. - Tudo passa por uma conexão. O envelope documentado é de centenas de arquivos SQL e dezenas de arquivos de dados, não cargas de múltiplos gigabytes. Dado em massa continua sendo trabalho de
COPY. - Os erros são os erros do PostgreSQL. Nenhuma taxonomia de erro específica de migração é sobreposta; falhas aparecem como SQLSTATEs. "Para um DBA isso normalmente é preferível", escreve o autor, mas para quem espera que a ferramenta interprete a falha, é um downgrade.
A leitura vale pela clareza do trade-off, mais do que pela adesão à ferramenta. Modelagem e integridade primeiro: quando o deploy é simples e linear, a ferramenta pronta é a escolha certa e mais barata. Quando o deploy ganha forma real, o argumento de que a política de deploy deveria ser SQL que o projeto revisa, versiona e testa na mesma transação do trabalho que governa é sólido, e força a pergunta certa antes de sair procurando a próxima flag.
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?