Savepoint to nazwana zakładka wewnątrz transakcji
Zwykła transakcja działa na zasadzie "wszystko albo nic": wszystko pomiędzy BEGIN a COMMIT albo trafia do bazy razem, albo razem zostaje wyrzucone. Zwykle właśnie o to chodzi, ale czasem przydaje się większa precyzja: "Spróbuj tej porcji zmian, a jeśli coś pójdzie źle, cofnij tylko ją i zachowaj resztę transakcji".
Tym właśnie jest savepoint. Wstawiasz nazwaną zakładkę, wykonujesz jakąś pracę, a potem albo ją zachowujesz (RELEASE), albo przewijasz do zakładki (ROLLBACK TO).
Obciążenie Ady i uznanie Borisa zostają. Błędna aktualizacja dla Nobody została wycofana bez utraty reszty transakcji.
Trzy polecenia
Całe API to trzy instrukcje:
SAVEPOINT name: ustawia zakładkę.RELEASE SAVEPOINT name: zachowuje pracę wykonaną od zakładki i usuwa zakładkę.ROLLBACK TO SAVEPOINT name: cofa wszystko od zakładki, a zakładka zostaje na miejscu, więc możesz spróbować ponownie.
Słowo SAVEPOINT po RELEASE i ROLLBACK TO jest opcjonalne: działają zarówno RELEASE risky, jak i ROLLBACK TO risky.
Z punktu widzenia końcowej bazy wiersz próba kroku 2 nigdy nie istniał. Cała reszta zostaje zapisana.
Savepointy bez zewnętrznej transakcji
Mały zwrot akcji: możesz wydać SAVEPOINT bez wcześniejszego BEGIN. SQLite po cichu otwiera za ciebie transakcję, a najbardziej zewnętrzny savepoint pełni rolę samej transakcji. RELEASE na tym savepoincie zatwierdza zmiany, a ROLLBACK TO przewija bez zatwierdzania.
Dlatego savepointy są czasem opisywane jako "nazwane transakcje". Jednak mieszanie obu stylów w prawdziwym kodzie wprowadza zamieszanie, więc wybierz jeden. Większość osób używa jawnego BEGIN ... COMMIT jako zewnętrznej granicy, a savepointów tylko jako wewnętrznych punktów częściowego cofania.
Zagnieżdżanie savepointów
Savepointy układają się w stos. Możesz ustawić jeden wewnątrz drugiego i wycofać wewnętrzny, nie ruszając zewnętrznego:
Końcowa zawartość: a, b, d. Wycofanie do inner usunęło c, ale praca wykonana przed inner (wstawienie b) została nietknięta, a transakcja trwała dalej.
Wycofanie do zewnętrznego savepointu odrzuca też wszystko, co zrobiono na wewnętrznych poziomach: cały stos powyżej tej nazwy zwija się naraz:
Zarówno b, jak i c zniknęły. ROLLBACK TO outer przewija wszystko od momentu ustawienia outer, łącznie z inner i wstawieniem c.
Po co używać savepointów?
Klasyczny przypadek to przetwarzanie porcji danych, w której pojedyncze elementy mogą się nie udać bez odrzucania całej porcji. Opakuj każdy element w savepoint, a jeśli coś się nie uda, wycofaj się do savepointu i idź dalej:
W prawdziwym kodzie aplikacji błędne wstawienie zgłasza błąd, a aplikacja go przechwytuje, wydaje ROLLBACK TO i kontynuuje. Dwa poprawne wiersze trafiają do bazy, a błędny nie psuje całej porcji.
W ten sam sposób ORM i narzędzia do migracji realizują zagnieżdżone transakcje: tak naprawdę nie zagnieżdżają bloków BEGIN (SQLite na to nie pozwala), tylko mapują zagnieżdżone wywołania na savepointy.
Na co uważać
Kilka szczegółów, na których potykają się osoby, które dopiero poznają savepointy:
COMMITzawsze zatwierdza całą transakcję. Nie ma znaczenia, ile savepointów jest otwartych:COMMIT(lub jego aliasEND) zamyka całą zewnętrzną transakcję. Nie traktujRELEASEjak częściowego zatwierdzenia: nic nie jest trwałe, dopóki otaczająca transakcja nie zostanie zatwierdzona.ROLLBACK(bezTO) przerywa wszystko. Kończy transakcję i odrzuca wszystkie otwarte savepointy. UżywajROLLBACK TO name, gdy chcesz, by transakcja trwała dalej.- Savepoint pozostaje otwarty, dopóki nie zostanie zwolniony albo wycofany. Zapomniane
RELEASEnie powoduje utraty danych, ale zakładka po prostu czeka do końca transakcji. - Nazwy nie muszą być unikalne. Jeśli ustawisz
SAVEPOINT sdwa razy,ROLLBACK TO sznajdzie najnowszy. Przydatne przy rekurencji, mylące, jeśli zdarzy się przypadkiem.
Dalej: widoki
Savepointy dają większą kontrolę nad zapisami. Kolejny krok to kształtowanie tego, jak czytasz: zapisanie zapytania jako nazwanego obiektu wielokrotnego użytku, z którego możesz robić SELECT jak z tabeli. To widok i o nim za chwilę.
Najczęściej zadawane pytania
Czym jest savepoint w SQLite?
Savepoint to nazwany znacznik, który ustawiasz wewnątrz transakcji. Później możesz zrobić ROLLBACK TO do tej nazwy, by cofnąć wszystko, co zrobiono po nim, albo RELEASE, by zachować zmiany i usunąć znacznik. Savepointy pozwalają traktować części transakcji jako mniejsze jednostki, które da się odzyskać.
Czym różni się savepoint od transakcji w SQLite?
Transakcja zaczyna się od BEGIN i kończy przez COMMIT lub ROLLBACK. Savepoint ustawia się wewnątrz transakcji przez SAVEPOINT name i daje punkt częściowego cofnięcia. Wycofanie do savepointu nie kończy otaczającej transakcji: możesz dalej pracować i zatwierdzić ją później.
Czy savepointy w SQLite można zagnieżdżać?
Tak. Możesz układać savepointy o różnych nazwach jeden na drugim, a ROLLBACK TO outer cofa wszystko aż do tego poziomu, łącznie z wewnętrznymi savepointami. Nazwy nie muszą być unikalne: SQLite używa najnowszego savepointu o danej nazwie, czyli standardowego zachowania stosu.