Un savepoint è un segnalibro con nome dentro una transazione
Una transazione normale è tutto o niente: tutto ciò che sta tra BEGIN e COMMIT viene salvato insieme oppure scartato insieme. Di solito è quello che vuoi, ma a volte ti serve una granularità più fine. "Prova questo gruppo di modifiche; se va storto, annulla solo quel gruppo e tieni vivo il resto della transazione."
È questo un savepoint. Lasci un segnalibro con nome, fai del lavoro e poi tieni il lavoro (RELEASE) oppure torni indietro al segnalibro (ROLLBACK TO).
L'addebito di Ada e l'accredito di Boris restano entrambi. L'aggiornamento sbagliato su Nobody è stato annullato senza perdere il resto della transazione.
I tre comandi
Tre istruzioni sono tutta l'API:
SAVEPOINT name: imposta un segnalibro.RELEASE SAVEPOINT name: tiene il lavoro fatto dal segnalibro in poi e rimuove il segnalibro.ROLLBACK TO SAVEPOINT name: annulla tutto ciò che è stato fatto dal segnalibro in poi; il segnalibro resta al suo posto, così puoi riprovare.
La parola SAVEPOINT dopo RELEASE e ROLLBACK TO è facoltativa: funzionano sia RELEASE risky sia ROLLBACK TO risky.
Per il database finale, la riga tentativo passo 2 non è mai esistita. Tutto il resto viene salvato.
Savepoint senza una transazione esterna
Ecco una piccola sorpresa: puoi usare SAVEPOINT senza un BEGIN prima. SQLite apre una transazione per te senza dirlo, e il savepoint più esterno fa la parte della transazione stessa. RELEASE su quel savepoint fa il commit, ROLLBACK TO torna indietro senza fare il commit.
Ecco perché a volte i savepoint vengono descritti come "transazioni con nome". Ma mescolare i due stili nel codice reale crea confusione: scegline uno. La maggior parte delle persone usa BEGIN ... COMMIT esplicito per il confine esterno e i savepoint solo per i punti interni di annullamento parziale.
Savepoint annidati
I savepoint si impilano. Puoi impostarne uno dentro un altro e annullare quello interno senza toccare quello esterno:
Contenuto finale: a, b, d. Tornare a inner ha rimosso c ma ha lasciato intatto il lavoro fatto prima di inner (l'insert di b), e la transazione è andata avanti.
Tornare a un savepoint esterno scarta anche tutto ciò che è stato fatto ai livelli interni: l'intera pila sopra quel nome si srotola in un colpo:
Sia b sia c sono spariti. ROLLBACK TO outer annulla tutto da quando è stato impostato outer, compresi inner e l'insert di c.
Perché usare i savepoint?
Il caso classico è l'elaborazione di un lotto in cui i singoli elementi possono fallire senza buttare via tutto il lotto. Racchiudi ogni elemento in un savepoint; se fallisce, torna al savepoint e prosegui:
Nel codice di un'applicazione reale, l'insert sbagliato solleva un errore e l'applicazione lo intercetta, esegue ROLLBACK TO e continua. Due righe buone vengono salvate; quella sbagliata non avvelena il lotto.
È anche così che ORM e strumenti di migrazione implementano le transazioni annidate: non annidano davvero i blocchi BEGIN (SQLite non lo permette), ma trasformano le chiamate annidate in savepoint.
Alcune cose a cui fare attenzione
Qualche dettaglio che frega chi usa i savepoint per la prima volta:
COMMITfa sempre il commit dell'intera transazione. Non importa quanti savepoint aperti hai:COMMIT(o il suo aliasEND) chiude tutta la transazione esterna. Non pensare aRELEASEcome a un commit parziale; niente è durevole finché la transazione che lo contiene non fa il commit.ROLLBACK(senzaTO) annulla tutto. Chiude la transazione e scarta tutti i savepoint aperti. UsaROLLBACK TO namequando vuoi tenere viva la transazione.- Un savepoint resta aperto finché non viene rilasciato o superato da un rollback. Dimenticare
RELEASEnon fa perdere dati, ma il segnalibro resta lì finché la transazione non finisce. - I nomi non devono essere unici. Se imposti
SAVEPOINT sdue volte,ROLLBACK TO strova il più recente. Utile per la ricorsione; fonte di confusione se capita per sbaglio.
Prossimo passo: le viste
I savepoint ti danno un controllo più fine sulle scritture. Il passo successivo è dare forma alle letture: salvare una query come oggetto riutilizzabile con un nome, da cui fare SELECT come da una tabella. È una vista, ed è il prossimo argomento.
Domande frequenti
Cos'è un savepoint in SQLite?
Un savepoint è un segnaposto con nome che imposti dentro una transazione. In seguito puoi fare ROLLBACK TO su quel nome per annullare tutto ciò che è stato fatto dopo, oppure RELEASE per tenere le modifiche ed eliminare il segnaposto. I savepoint ti permettono di trattare parti di una transazione come unità più piccole e recuperabili.
Che differenza c'è tra un savepoint e una transazione in SQLite?
Una transazione inizia con BEGIN e finisce con COMMIT o ROLLBACK. Un savepoint si imposta dentro una transazione con SAVEPOINT name e ti dà un punto di annullamento parziale. Tornare a un savepoint non chiude la transazione che lo contiene: puoi continuare a lavorare e fare il commit più tardi.
I savepoint di SQLite possono essere annidati?
Sì. Puoi impilare savepoint con nomi diversi, e ROLLBACK TO outer annulla tutto fino a quel livello, compresi i savepoint interni. I nomi non devono essere unici: SQLite usa il savepoint più recente con quel nome, il classico comportamento a pila.