Course topics

By WebNest Studio Engineering Team

PostgreSQL Tutorial

Transactions and SAVEPOINT in PostgreSQL

A transaction groups one or more SQL statements into a single all-or-nothing unit of work. PostgreSQL guarantees full ACID compliance: either every statement in the transaction succeeds and is made permanent with COMMIT, or none of them take effect, undone entirely with ROLLBACK. This is essential whenever multiple related changes must succeed or fail together — such as debiting one account and crediting another.

PostgreSQL also has a distinctive behavior worth knowing: once any statement inside a transaction produces an error, the entire transaction is marked as aborted, and every subsequent statement is rejected until you issue a ROLLBACK — even statements that would otherwise have succeeded.

Why it matters

PostgreSQL is stricter than some databases: after any error inside a transaction it refuses every further statement until you roll back. That protects your data and surprises newcomers. Savepoints are the tool for recovering from an expected error without throwing the whole transaction away.

BEGIN, COMMIT, and ROLLBACK

A transaction starts with BEGIN; (or START TRANSACTION;), after which every statement is provisional. COMMIT; makes all of those changes permanent and visible to other connections. ROLLBACK; discards every change made since BEGIN, as if none of it had happened. Outside of an explicit BEGIN, PostgreSQL runs every individual statement in its own implicit transaction, which is why a single INSERT without BEGIN is already atomic on its own.

The "Aborted Transaction" Behavior

If any statement inside a BEGIN block raises an error, PostgreSQL puts the whole transaction into an aborted state. You will see the error current transaction is aborted, commands ignored until end of transaction block for every further statement, even valid ones, until you run ROLLBACK. This surprises developers coming from databases with more forgiving error handling, and it is precisely why SAVEPOINT exists.

SAVEPOINT: Partial Rollback

SAVEPOINT name; marks a point inside a transaction that you can roll back to without discarding the entire transaction. ROLLBACK TO SAVEPOINT name; undoes everything since that savepoint (and clears the aborted state, if the error happened after it) while keeping earlier work in the transaction intact. RELEASE SAVEPOINT name; discards the savepoint once you no longer need to roll back to it. This is especially useful for handling an expected failure in one step of a larger transaction — such as attempting an INSERT that might violate a unique constraint — without losing everything else already done in that transaction.

Examples

A transaction transferring funds between two accounts atomically

SQL
BEGIN;

UPDATE accounts SET balance = balance - 200 WHERE id = 1;
UPDATE accounts SET balance = balance + 200 WHERE id = 2;

COMMIT;
Output
BEGIN
UPDATE 1
UPDATE 1
COMMIT

Using SAVEPOINT to recover from an expected error mid-transaction

SQL
BEGIN;

INSERT INTO categories (name) VALUES ('Books');

SAVEPOINT before_duplicate;
INSERT INTO categories (name) VALUES ('Laptops'); -- violates UNIQUE, fails
ROLLBACK TO SAVEPOINT before_duplicate;

INSERT INTO categories (name) VALUES ('Stationery');

COMMIT;
Output
BEGIN
INSERT 0 1
SAVEPOINT
ERROR:  duplicate key value violates unique constraint "categories_name_key"
ROLLBACK
INSERT 0 1
COMMIT

Common Mistakes

  • Continuing to run statements after an error inside a transaction without a prior SAVEPOINT, then being confused by "current transaction is aborted" errors on every subsequent statement.
  • Forgetting to COMMIT (or explicitly ROLLBACK) an open transaction, leaving it idle and holding locks that block other connections.
  • Assuming SAVEPOINT alone is enough without ROLLBACK TO SAVEPOINT — creating a savepoint does nothing by itself; it only marks a point you can return to.
  • Treating a multi-statement operation as "probably fine without BEGIN," when a failure partway through leaves the database in an inconsistent, partially-applied state.

Exercise

Inside one transaction, insert product 1. Then set a savepoint and try to insert a second row that reuses id 1, which fails. Roll back to the savepoint, insert product 2 instead, and commit. Count the rows.

Expected output: total then 2

SQL · starter code
CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL);

BEGIN;
INSERT INTO products (id, name) VALUES (1, 'Pen');

-- TODO: create a savepoint named before_second

INSERT INTO products (id, name) VALUES (1, 'Duplicate');

-- TODO: roll back to the savepoint

INSERT INTO products (id, name) VALUES (2, 'Bag');
COMMIT;

SELECT COUNT(*) AS total FROM products;
Show hints
  • The statements are SAVEPOINT name and ROLLBACK TO SAVEPOINT name.
  • Rolling back to a savepoint undoes only what happened after it and keeps the transaction open.
Show solution
SQL · solution
CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL);

BEGIN;
INSERT INTO products (id, name) VALUES (1, 'Pen');

SAVEPOINT before_second;

INSERT INTO products (id, name) VALUES (1, 'Duplicate');

ROLLBACK TO SAVEPOINT before_second;

INSERT INTO products (id, name) VALUES (2, 'Bag');
COMMIT;

SELECT COUNT(*) AS total FROM products;

Quick quiz

1. In PostgreSQL, a statement fails inside a transaction. What happens to the statements that follow?

  • A. They run normally
  • B. They are rejected until the transaction is rolled back, or rolled back to a savepoint
  • C. The transaction commits automatically
  • D. The failed statement is retried
Show answer

B. The message is "current transaction is aborted, commands ignored until end of transaction block".

2. What does ROLLBACK TO SAVEPOINT sp do?

  • A. Ends the transaction
  • B. Commits the work so far
  • C. Undoes the work done after the savepoint and keeps the transaction open
  • D. Deletes the savepoint only
Show answer

C. Work done before the savepoint is kept.

3. Can CREATE TABLE be rolled back in PostgreSQL?

  • A. Yes; most DDL is transactional
  • B. No, DDL always commits immediately
  • C. Only for temporary tables
  • D. Only with a savepoint
Show answer

A. A migration can therefore be applied completely or not at all.

Interview questions

What happens after an error inside a PostgreSQL transaction?

Show answer

The transaction enters an aborted state. Every further statement fails with "current transaction is aborted, commands ignored until end of transaction block" until the transaction is ended with ROLLBACK, or rolled back to a savepoint that was set before the error. Even COMMIT at that point performs a rollback. This guarantees that a partly failed transaction can never be committed by accident.

What is transactional DDL and why is it valuable?

Show answer

In PostgreSQL, statements that change the schema, such as CREATE TABLE, ALTER TABLE and DROP INDEX, take part in transactions and can be rolled back. A migration made of several schema changes can therefore be wrapped in one transaction: if any step fails, the database returns to its previous schema and is never left half migrated. Databases that commit implicitly on DDL cannot offer this.

Key Points to Remember

  • BEGIN...COMMIT/ROLLBACK groups statements into one atomic, all-or-nothing unit of work.
  • Once a statement inside a transaction errors, PostgreSQL aborts the entire transaction until ROLLBACK is issued.
  • SAVEPOINT lets you roll back part of a transaction without discarding everything already done.
  • Outside an explicit transaction, every individual statement runs in its own implicit transaction.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs your SQL on SQLite in your browser. Standard SQL works; a few features that exist only in PostgreSQL or MySQL do not. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

These examples need a project environment on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering