Para escrever SQL com IA com segurança, dê ao agente o seu schema real e um usuário de banco somente leitura, faça-o mostrar o plano da query e mantenha cada migration pequena, reversível e testada numa cópia dos dados antes de chegar perto da produção. Um agente de código com IA escreve queries e migrations rápido. O risco não é ele escrever um SQL ruim de vez em quando, e sim um comando ruim rodar onde pode causar estrago.
Este guia mostra como pedir queries, como conferi-las com EXPLAIN, como pedir índices, como escrever migrations que não travam nem perdem dados, como os ORMs entram nisso e como conectar um agente a um banco via MCP sem entregar as chaves da produção. Os exemplos usam PostgreSQL; as ideias valem para MySQL e SQLite.
Comece pelo schema, não pela pergunta
O motivo mais comum para um agente escrever uma query errada é ter chutado: uma coluna chamada user_id que na verdade é account_id, um status guardado como inteiro, uma flag de soft delete que ele não conhecia. Dê o schema primeiro.
Onde conseguir:
- Arquivos de schema do ORM.
prisma/schema.prisma, seus arquivos de schema do Drizzle,models.pydo Django,db/schema.rbdo Rails. O agente consegue lê-los diretamente; indique-os no prompt. - Um dump só da estrutura.
pg_dump --schema-onlygrava tabelas, colunas, índices e constraints sem nenhum dado:
pg_dump --schema-only --no-owner "$DEV_DATABASE_URL" > schema.sql
- Uma conexão de banco somente leitura via servidor MCP, para o agente listar tabelas e inspecionar colunas sozinho (mais sobre isso abaixo).
Depois acrescente o que o schema não diz: o tamanho aproximado das tabelas, quais colunas costumam ser NULL e o que significam os termos do negócio. “Cliente ativo é quem fez um pedido nos últimos 90 dias” evita um chute errado.
Um prompt para uma query:
Using schema.sql: write a PostgreSQL query that returns, for each country,
the number of active customers (an order in the last 90 days) and their
total order value this year. Exclude soft-deleted customers
(deleted_at IS NOT NULL). orders has about 20M rows, customers 400k.
Run it on the dev database, show me the first 10 rows and the EXPLAIN plan.
Use um usuário somente leitura para o agente
Antes de um agente rodar qualquer coisa num banco, dê a ele um usuário próprio que não consegue alterar dados. No PostgreSQL 14 ou mais novo, o papel nativo pg_read_all_data deixa isso curto:
CREATE ROLE agent_ro LOGIN PASSWORD 'change-me';
GRANT pg_read_all_data TO agent_ro;
ALTER ROLE agent_ro SET default_transaction_read_only = on;
ALTER ROLE agent_ro SET statement_timeout = '30s';
pg_read_all_data dá acesso de leitura a todas as tabelas, sem direito de escrita. As duas linhas ALTER ROLE são uma segurança extra: as transações começam somente leitura, e uma query descontrolada é cancelada depois de 30 segundos. Em versões mais antigas ou para um acesso mais restrito, conceda CONNECT, USAGE no schema e SELECT nas tabelas que quiser.
Aponte esse usuário para um banco de desenvolvimento ou staging com dados realistas, não para a produção. Se precisar de números da produção, use uma réplica de leitura ou uma cópia recente anonimizada.
Leia o plano da query
Uma query que retorna as linhas certas ainda pode levar minutos. Peça ao agente para rodar EXPLAIN em tudo que toca uma tabela grande e explicar o plano para você:
EXPLAIN (ANALYZE, BUFFERS)
SELECT c.country, count(DISTINCT c.id)
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.created_at > now() - interval '90 days'
AND c.deleted_at IS NULL
GROUP BY c.country;
EXPLAIN sozinho mostra o plano que o banco usaria. ANALYZE executa de fato a query e acrescenta tempos e contagens de linhas reais. Isso importa: EXPLAIN ANALYZE num UPDATE ou DELETE altera os dados de verdade. Coloque-os numa transação que você desfaz com rollback, ou rode só numa cópia.
O que pedir para o agente procurar:
- Sequential scans em tabelas grandes quando você filtra poucas linhas.
- Linhas estimadas vs reais muito distantes, o que indica estatísticas desatualizadas (
ANALYZE tablenameas atualiza). - Sorts ou hashes indo para o disco.
- Nested loops sobre muitas linhas onde um hash join sairia mais barato.
Um bom prompt de acompanhamento: “Explique este plano em palavras simples. Qual etapa leva mais tempo e por quê? Proponha uma mudança e mostre o novo plano.”
Peça índices, com o custo-benefício
Agentes adoram sugerir índices. Cada um acelera algumas leituras, deixa mais lenta toda escrita naquela tabela e ocupa espaço em disco. Peça a justificativa:
The query above does a sequential scan on orders. Suggest at most one
index, explain which queries it helps and what it costs on writes,
and show the EXPLAIN plan with it on the dev database.
Numa tabela movimentada, crie o índice sem bloquear escritas:
CREATE INDEX CONCURRENTLY idx_orders_customer_created
ON orders (customer_id, created_at);
CONCURRENTLY demora mais, mas não bloqueia inserts e updates enquanto roda. Ele não pode rodar dentro de um bloco de transação, o que importa porque muitas ferramentas de migration envolvem cada migration numa transação; veja como a sua ferramenta lida com isso (a maioria tem uma opção para desligar a transação numa migration específica). Se uma criação concorrente falhar, ela deixa para trás um índice inválido que você precisa remover e recriar.
Escreva migrations seguras de rodar
Uma migration roda sobre dados vivos, muitas vezes enquanto o app está atendendo tráfego. Estes são os padrões para pedir ao seu agente e para conferir no que ele entregar.
Expand, migrate, contract
Não renomeie nem remova num passo só. Para renomear users.name para users.full_name:
- Expand: adicione
full_namecomo coluna nullable. Faça deploy de código que escreve nas duas colunas. - Migrate: preencha
full_namea partir denameem lotes. Passe as leituras parafull_name. - Contract: numa versão posterior, quando nada mais ler
name, remova-a.
Cada passo pode ir para deploy e ser revertido isoladamente. Um único ALTER TABLE ... RENAME COLUMN quebra toda instância do código antigo em execução no momento em que roda.
Evite locks longos
Algumas DDLs pegam um lock que bloqueia leituras ou escritas enquanto rodam. Peça ao agente para sinalizá-las e usar a forma mais segura:
| Arriscado | Mais seguro |
|---|---|
CREATE INDEX numa tabela grande | CREATE INDEX CONCURRENTLY |
| Adicionar uma foreign key num passo só | ADD CONSTRAINT ... NOT VALID, depois VALIDATE CONSTRAINT num passo separado |
SET NOT NULL numa tabela grande | Adicione um CHECK (col IS NOT NULL) NOT VALID, valide-o e então aplique NOT NULL |
| Mudar o tipo de uma coluna | Adicione uma coluna nova, preencha, troque e remova a antiga |
Um UPDATE gigante para um backfill | Lotes de alguns milhares de linhas |
Desde o PostgreSQL 11, adicionar uma coluna com default constante não reescreve a tabela, então isso costuma ser barato. Defina também um lock timeout no topo da migration, para ela falhar rápido em vez de ficar na fila atrás de uma query longa e bloquear tudo o que vem depois:
SET lock_timeout = '5s';
Torne-a reversível
Toda migration deve ter um caminho de volta, seja uma down migration ou um passo de rollback por escrito. Peça explicitamente: “Escreva a migration up e down e me diga quais passos não podem ser desfeitos sem perda de dados.” Remover uma coluna não se desfaz com uma down migration; os dados já foram. Esse é mais um motivo para remover num passo posterior e separado, depois de um backup.
Teste numa cópia
Rode a migration numa cópia dos dados antes da produção: um banco de staging restaurado de um backup recente, ou um banco local com dados falsos no volume da produção. Confira três coisas: que ela termina, quanto tempo leva e que o app continua funcionando com o novo schema. Depois rode a down migration e confira também.
Usando um ORM: deixe-o gerar, peça ao agente para revisar
Se você usa um ORM, o fluxo mais seguro é: mude o model, deixe o ORM gerar a migration e peça ao agente para revisar o SQL gerado.
| ORM | Gerar uma migration | Aplicar | Ver o SQL |
|---|---|---|---|
| Prisma | npx prisma migrate dev --name add_invoice --create-only | npx prisma migrate deploy | o arquivo migration.sql que ele cria |
| Drizzle | npx drizzle-kit generate | npx drizzle-kit migrate | os arquivos SQL na sua pasta de migrations |
| Django | python manage.py makemigrations | python manage.py migrate | python manage.py sqlmigrate app 0004 |
| Rails | bin/rails generate migration AddFullNameToUsers | bin/rails db:migrate | db/schema.rb e o arquivo da migration |
O --create-only do Prisma grava a migration sem aplicá-la, para você editar o SQL antes, por exemplo para adicionar CONCURRENTLY ou dividir um passo. O Drizzle também tem drizzle-kit push, que aplica mudanças de schema diretamente sem arquivos de migration; é prático para protótipos locais, mas use migrations geradas para tudo que for compartilhado.
Um prompt de revisão que funciona com qualquer um deles:
Read the new migration in prisma/migrations/. For each statement, tell me:
does it lock the table, can it lose data, how long will it take on a
table with 20M rows, and is there a safer form? Don't change any files.
Conectando um agente a um banco com MCP
Um servidor MCP deixa o agente consultar o banco sozinho: listar tabelas, rodar um SELECT, ler um plano. Isso é útil, e também é onde mora o risco, então comece no modo somente leitura.
O Supabase tem um servidor MCP hospedado. Em outubro de 2026, adicionar read_only=true à URL faz toda query rodar como um usuário Postgres somente leitura, e project_ref amarra o servidor a um projeto:
claude mcp add --transport http supabase \
"https://mcp.supabase.com/mcp?project_ref=<your-project-ref>&read_only=true"
A orientação do próprio Supabase é conectar um projeto de produção só quando a tarefa realmente precisa de dados de produção, e mesmo assim com escopo por projeto e modo somente leitura ativados; manter a aprovação manual das chamadas de ferramentas; e revisar os resultados antes de agir com base neles, porque as proteções contra prompt injection não são infalíveis.
PostgreSQL puro: use um servidor MCP de Postgres mantido, com o seu usuário agent_ro na connection string. Melhores servidores MCP para programar lista opções e suas configurações de somente leitura. O antigo servidor Postgres de referência do projeto MCP foi arquivado.
As regras que vale escrever no seu CLAUDE.md ou AGENTS.md:
## Database
- The database MCP server is read-only. Never ask for write access.
- Schema changes go through migrations in prisma/migrations, never direct DDL.
- Never run UPDATE or DELETE without a WHERE clause.
- Run new queries with EXPLAIN on the dev database before proposing them.
Nunca dê a um agente acesso de escrita à produção
Esta é a regra que não se dobra. Linhas de dados podem conter texto que parece instrução (um ticket de suporte, a bio de um usuário), e um agente que as lê pode segui-las; isso é prompt injection. Um pedido mal interpretado pode virar um DELETE com o filtro errado. Com um usuário somente leitura, o pior caso é uma query lenta. Com acesso de escrita, é uma queda ou perda de dados.
A divisão segura: o agente escreve e testa o SQL com dados de desenvolvimento; você revisa; uma ferramenta de migration ou uma pessoa aplica na produção. Segurança de agentes de código com IA trata de sandbox e permissões de forma mais ampla.
Fazendo isso no AgentVera
O AgentVera é um app desktop para macOS, Windows e Linux que roda Claude Code, Codex e outros agentes de CLI lado a lado. Alguns recursos foram feitos exatamente para este trabalho:
- Gerenciador de banco de dados: conexões PostgreSQL, MySQL/MariaDB e SQLite na mesma janela dos seus agentes, com uma visão de tabela editável, um editor SQL com
EXPLAIN, um designer de tabelas que mostra a DDL antes de aplicá-la e um diagrama ER. Conexões marcadas como somente leitura também são abertas em modo somente leitura no lado do servidor, e uma conexão marcada como produção aparece em vermelho, com as alterações pedindo confirmação antes. Você pode deixar os agentes usarem uma conexão pela ferramenta embutida agentvera-db: o acesso vem desligado por padrão, começa em modo somente leitura quando você o liga, os agentes nunca veem a senha e toda query que rodam fica registrada. O gerenciador de banco de dados está nos planos Pro e Team. - DB Guard: quando um agente termina um turno, os arquivos de migration alterados são analisados em busca de riscos como
DROP,TRUNCATE,UPDATEouDELETEsemWHERE,CREATE INDEXsemCONCURRENTLY, novas constraints e passos de down faltando. Com uma conexão parecida com a produção selecionada, o risco é dimensionado pelo tamanho real das tabelas. Você pode testar a migration num banco shadow dentro de uma transação que sempre sofre rollback, ou mandar os achados de volta ao agente para corrigir. - Gerenciador de MCP: configure um servidor MCP de banco uma vez e ele é conectado aos seus agentes a cada inicialização.
- Code review: um segundo modelo revisa o código de migrations e queries que o agente escreveu quando o turno dele termina.
Baixe o AgentVera e experimente num banco de desenvolvimento.
Conclusão
Dê ao agente o schema real e um usuário somente leitura com dados de desenvolvimento. Peça que ele rode EXPLAIN e explique o plano, que justifique cada índice e que escreva migrations em passos pequenos e reversíveis de expand and contract, com DDL sem bloqueio. Deixe o ORM gerar as migrations e o agente revisá-las, teste cada uma numa cópia e mantenha as escritas na produção nas suas próprias mãos. Instruções claras facilitam tudo isso; como escrever prompts para agentes de código com IA aprofunda contexto, planos e critérios de conclusão.