Menu

SAVEPOINT no SQLite: transações aninhadas com ROLLBACK TO

Como funcionam os savepoints no SQLite: marcadores nomeados dentro de uma transação que você pode desfazer sem perder tudo o que foi feito antes.

Esta página tem editores executáveis — edite, execute e veja a saída na hora.

Um SAVEPOINT é um marcador nomeado dentro de uma transação

Uma transação comum funciona no esquema tudo-ou-nada: tudo o que está entre o BEGIN e o COMMIT é gravado junto ou descartado junto. Na maioria das vezes é exatamente isso que você quer — mas, em algumas situações, faz falta um controle mais granular. Algo como: "vou tentar essa leva de alterações; se der ruim, desfaço só essa parte e mantenho o resto da transação viva".

É aí que entra o savepoint do SQLite. Você cria um marcador nomeado, executa um trecho de trabalho e depois decide: ou confirma esse pedaço com RELEASE, ou volta até o marcador com ROLLBACK TO.

O débito da Ada e o crédito do Boris permanecem aplicados. Já o UPDATE errado em Nobody foi desfeito sem perder o resto da transação.

Os três comandos

A API toda se resume a três comandos:

  • SAVEPOINT nome — cria um marcador.
  • RELEASE SAVEPOINT nome — confirma o que foi feito desde o marcador e descarta o marcador.
  • ROLLBACK TO SAVEPOINT nome — desfaz tudo que aconteceu depois do marcador, mas mantém o marcador no lugar para você tentar de novo.

A palavra SAVEPOINT depois de RELEASE e ROLLBACK TO é opcional — tanto RELEASE risky quanto ROLLBACK TO risky funcionam normalmente.

A linha tentativa do passo 2 simplesmente não existe do ponto de vista do banco final. Todo o resto é persistido.

Savepoints sem uma transação externa

Aqui vai um detalhe curioso: dá pra usar SAVEPOINT sem precisar de um BEGIN antes. O SQLite abre uma transação por baixo dos panos, e o savepoint mais externo acaba fazendo o papel da própria transação. Um RELEASE nesse savepoint faz o commit, enquanto um ROLLBACK TO desfaz tudo sem commitar.

É por isso que savepoints são às vezes descritos como "transações nomeadas". Mas misturar os dois estilos no mesmo código vira uma bagunça — escolha um. A maioria das pessoas usa BEGIN ... COMMIT explícito para delimitar a transação externa e deixa os savepoints só para os pontos internos onde pode ser preciso desfazer parcialmente.

Savepoints aninhados no SQLite

Savepoints empilham. Você pode criar um dentro do outro e fazer rollback do interno sem mexer no externo:

Resultado final: a, b, d. O ROLLBACK TO SAVEPOINT inner desfez o c, mas preservou o que foi feito antes do inner (o insert do b), e a transação seguiu seu curso.

Voltar para um savepoint externo também descarta tudo o que foi feito nos níveis internos — toda a pilha acima daquele nome é desfeita de uma só vez:

Tanto b quanto c desapareceram. O ROLLBACK TO outer desfaz tudo o que aconteceu depois que outer foi criado, incluindo o inner e o insert do c.

Quando usar SAVEPOINT no SQLite?

O caso clássico é o processamento em lote, onde itens individuais podem falhar sem comprometer o lote inteiro. A ideia é envolver cada item em um savepoint: se algo der errado, você faz rollback até aquele savepoint e segue em frente:

Em código de aplicação real, o INSERT problemático dispara um erro, a aplicação captura essa exceção, executa ROLLBACK TO e segue em frente. As duas linhas válidas são gravadas; a linha ruim não contamina o lote.

Esse padrão também é como ORMs e ferramentas de migração implementam transações aninhadas — eles não aninham blocos BEGIN de verdade (o SQLite não permite isso), mas sim mapeiam chamadas aninhadas para savepoints.

Alguns detalhes para ficar de olho

Algumas pegadinhas que costumam confundir quem está começando com savepoints:

  • COMMIT sempre confirma a transação inteira. Não importa quantos savepoints estejam abertos — COMMIT (ou seu apelido END) fecha a transação externa por completo. Não encare RELEASE como um commit parcial; nada se torna durável até a transação que envolve tudo ser commitada.
  • ROLLBACK (sem TO) aborta tudo. Encerra a transação e descarta todos os savepoints abertos. Quando você quiser manter a transação viva, use ROLLBACK TO nome.
  • Um savepoint permanece aberto até ser liberado ou desfeito por um rollback. Esquecer de dar RELEASE não causa perda de dados, mas o marcador fica ali parado até a transação terminar.
  • Os nomes não precisam ser únicos. Se você criar SAVEPOINT s duas vezes, ROLLBACK TO s vai encontrar o mais recente. Útil para recursão; confuso se acontecer sem querer.

A seguir: Views

Savepoints dão controle mais fino sobre as escritas. O próximo passo é moldar como você — salvar uma consulta como um objeto nomeado e reutilizável, do qual você pode dar SELECT como se fosse uma tabela. Isso é uma view, e é o que vem em seguida.

Perguntas frequentes

O que é um savepoint no SQLite?

É um marcador nomeado que você cria dentro de uma transação. Depois, dá para usar ROLLBACK TO com esse nome para desfazer tudo o que rolou após o marcador, ou RELEASE para manter as alterações e descartar o marcador. Na prática, savepoints permitem tratar partes da transação como blocos menores e recuperáveis.

Qual a diferença entre savepoint e transação no SQLite?

Uma transação começa com BEGIN e termina em COMMIT ou ROLLBACK. Já o savepoint é criado dentro dessa transação, com SAVEPOINT nome, e funciona como um ponto de desfazer parcial. Voltar até um savepoint não encerra a transação que o envolve — você continua trabalhando normalmente e faz o commit depois.

Dá para aninhar savepoints no SQLite?

Dá sim. Você pode empilhar vários savepoints com nomes diferentes, e um ROLLBACK TO outer desfaz tudo até aquele nível, inclusive os savepoints internos. Os nomes nem precisam ser únicos: o SQLite usa o savepoint mais recente com aquele nome, seguindo o comportamento clássico de pilha.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR