


SAVEPOINT создаёт точку внутри открытой транзакции PostgreSQL. Команда ROLLBACK TO SAVEPOINT отменяет изменения после этой точки и позволяет продолжить транзакцию. Изменения до точки остаются, но окончательно сохраняются только при COMMIT.
В упражнении Mentoring.digital используем временную таблицу и вымышленные задачи. Выполняйте команды в отдельном учебном соединении, где нет незавершённой рабочей транзакции.
Откройте одну сессию PostgreSQL и выполняйте команды в указанном порядке. Временная таблица нужна только этому соединению. Вставка строки 1 происходит до точки, вставка строки 2 после неё.
CREATE TEMP TABLE savepoint_demo (
id integer PRIMARY KEY,
title text NOT NULL
);
BEGIN;
INSERT INTO savepoint_demo VALUES (1, 'Основная задача');
SAVEPOINT optional_step;
INSERT INTO savepoint_demo VALUES (2, 'Черновой вариант');
ROLLBACK TO SAVEPOINT optional_step;
INSERT INTO savepoint_demo VALUES (3, 'Исправленный вариант');
RELEASE SAVEPOINT optional_step;
COMMIT;
SELECT id, title FROM savepoint_demo ORDER BY id;
Итоговый SELECT должен вернуть строки 1 и 3. Строка 2 отменена. ROLLBACK TO не завершил всю транзакцию, поэтому после него удалось вставить исправленный вариант и выполнить COMMIT.
Теперь намеренно нарушим первичный ключ. Этот фрагмент выполняйте по одному оператору: некоторые клиенты прекращают исполнение всего скрипта при первой ошибке. После сообщения о дублирующемся ключе отдельно выполните ROLLBACK TO и оставшиеся команды.
BEGIN;
SAVEPOINT before_attempt;
INSERT INTO savepoint_demo VALUES (1, 'Повторный id');
-- Ожидаемая ошибка: id 1 уже существует.
ROLLBACK TO SAVEPOINT before_attempt;
INSERT INTO savepoint_demo VALUES (4, 'Новая задача');
RELEASE SAVEPOINT before_attempt;
COMMIT;
После ошибки текущая транзакция не готова принимать обычные команды до отката. Если точка была создана заранее и ошибка допускает восстановление соединения, откат к ней позволяет продолжить. Без подходящей точки потребуется откатить всю транзакцию. Потерянное соединение таким способом не восстановить.
RELEASE SAVEPOINT удаляет точку и связанные с ней более поздние точки, сохраняя текущие изменения внутри транзакции. Внешний ROLLBACK всё ещё может их отменить. ROLLBACK TO, напротив, отменяет более поздние изменения и точки, но оставляет саму указанную точку доступной.
Поэтому в коде нужно различать три действия: вернуться к точке, убрать ненужную точку, завершить транзакцию. Используйте разные понятные имена точек. Повторяющиеся имена разрешены PostgreSQL, но усложняют понимание того, к какой именно границе возвращается код.
Откат базы не отменит уже отправленное письмо или внешний HTTP-запрос. Значения последовательностей также не обязаны возвращаться назад, поэтому пропуск в сгенерированных идентификаторах сам по себе не доказывает потерю строки. Для внешних действий нужен отдельный согласованный механизм.
Не оборачивайте каждый оператор в SAVEPOINT без причины. Сначала определите, какой шаг допустимо отменить отдельно и сохраняет ли продолжение бизнес-правила. Если без неудачного шага результат не имеет смысла, правильнее отменить всю операцию.
Для закрепления повторите первый пример с полным ROLLBACK вместо COMMIT и сравните результат. Транзакции можно отработать на курсе SQL с наставником, а границы реальной операции обсудить с ментором по SQL.