47 Savepoints
A savepoint is a named point inside a transaction. You can ROLLBACK TO SAVEPOINT name to undo work done after that point while keeping everything before it. Savepoints are how PostgreSQL implements nested-transaction-like semantics — useful when you want to try a risky step, recover from an error, and continue, or when application code emulates nested transactions.
What you’ll learn
- Setting a savepoint with
SAVEPOINT name - Rolling back to a savepoint
- Releasing a savepoint
- Recovering from an error inside a transaction
- The relationship to
BEGIN/COMMIT
Example
SAVEPOINT name marks a point. ROLLBACK TO SAVEPOINT name undoes everything between the savepoint and now, but the transaction continues — you can keep doing work and eventually COMMIT. RELEASE SAVEPOINT name forgets the savepoint (you cannot roll back to it anymore) but does not undo any work.
Savepoints are how application frameworks emulate “nested transactions”: when application code calls begin_transaction while already inside one, the driver sets a savepoint instead of starting a new top-level transaction. A “rollback” inside the inner block rolls back to that savepoint.
The error-recovery use case is important. Inside a transaction, any error puts the transaction in an “aborted” state where every subsequent statement fails until you ROLLBACK. With savepoints, you can ROLLBACK TO SAVEPOINT before_risky and continue — the transaction is back to a usable state.
Run the example
To run:
Common pitfalls
- Savepoints have overhead. A transaction with thousands of savepoints (a common ORM pattern) can be expensive.
- After
ROLLBACK TO SAVEPOINT s, the savepointsitself still exists — you can roll back to it again. To remove it, useRELEASE. RELEASE SAVEPOINT sdoes not “commit” the work — onlyCOMMIT(of the enclosing transaction) does.RELEASEjust removes the marker.- Savepoints only exist inside an explicit transaction.
SAVEPOINT s;outside of one is an error.
ORMs often use savepoints to implement “transactional” tests where each test runs inside a savepoint that gets rolled back, leaving the database untouched. Look up pytest-postgresql, RSpec’s transactional fixtures, etc.
Add a second savepoint, insert more rows, then ROLLBACK TO the first savepoint and see all the work after it gone. Try the error-recovery pattern: BEGIN; INSERT ... SAVEPOINT s; INSERT (bad row); ROLLBACK TO SAVEPOINT s; INSERT (good row); COMMIT;.
Downloads
SQL file: savepoints.sql · All lesson files · Setup guide