2010-03-11 27 views
10

Provenendo dal mondo MS SQL, tendo a fare un uso pesante delle stored procedure. Al momento sto scrivendo un'applicazione che usa molte funzioni di plpgsql di PostgreSQL. Quello che mi piacerebbe fare è il rollback di tutti gli INSERTI/AGGIORNAMENTI contenuti in una particolare funzione se ottengo un'eccezione in qualsiasi punto al suo interno.PostgreSQL: rollback di una transazione all'interno di una funzione plpgsql?

Inizialmente avevo l'impressione che ogni funzione fosse racchiusa nella propria transazione e che un'eccezione avrebbe automaticamente ripristinato tutto. Tuttavia, questo non sembra essere il caso. Mi chiedo se dovrei usare i punti di salvataggio in combinazione con la gestione delle eccezioni, invece? Ma non capisco davvero la differenza tra una transazione e un punto di salvataggio per sapere se questo è l'approccio migliore. Qualche consiglio per favore?

CREATE OR REPLACE FUNCTION do_something(
     _an_input_var int 
       ) RETURNS bool AS $$ 
     DECLARE 
       _a_variable int; 
     BEGIN 
       INSERT INTO tableA (col1, col2, col3) 
         VALUES (0, 1, 2); 

       INSERT INTO tableB (col1, col2, col3) 
         VALUES (0, 1, 'whoops! not an integer'); 

       -- The exception will cause the function to bomb, but the values 
       -- inserted into "tableA" are not rolled back.  

       RETURN True; 
END; $$ LANGUAGE plpgsql; 
+0

Puoi pubblicare un esempio di una funzione che non esegue il rollback di tutto come ti aspetteresti? Le funzioni PL/pgSQL * do * vengono eseguite all'interno del contesto della transazione dell'istruzione chiamante, ma un blocco BEGIN..EXCEPTION può modificare tale comportamento. Senza vedere un esempio, è difficile dare il consiglio adeguato. –

+0

Modificato per aggiungere un esempio. Grazie. – jamieb

+0

Sto eseguendo 8.4.2, creato tableA e B con tre colonne int ciascuna, esegui il tuo esempio (con ";" rimosso alla fine di INSERT INTO nella riga della tabellaB) e bombardato. Ho controllato entrambi i tavoli e erano entrambi vuoti. Ho anche aggiunto un po 'di codice di debug tra i due per verificare che il record fosse lì prima che fallisse, quindi non c'era più dopo l'errore. –

risposta

13

Una funzione fa rappresenta una transazione. Non è necessario eseguire il wrapping di una funzione in BEGIN/COMMIT.

+7

Questo non è vero per PostgreSQL. Una singola istruzione SQL, quando non eseguita come parte della transazione esplicita (non viene eseguita tra BEGIN e COMMIT/END), viene eseguita come una singola transazione. Ma se tale affermazione richiama più funzioni, tutte le funzioni vengono eseguite in un'unica transazione. Inoltre, quando si hanno più transazioni di istruzioni (BEGIN, COMMIT espliciti) e tali istruzioni richiedono alcune procedure, tutte queste verranno eseguite nelle singole transazioni. Come altri hanno già detto: i punti di salvataggio sono la strada da percorrere. –

+2

@Jacek: Joshua ha ragione, una funzione rappresenta la propria transazione. Non è necessario un punto di salvataggio per eseguire il rollback di entrambi gli inserimenti, in caso di errore entrambi falliranno. –

+10

@Frank: ma questo è dovuto alla transazione esterna. Anche gli inserimenti in altre funzioni richiamati nella stessa istruzione o nelle precedenti dichiarazioni nella transazione avranno esito negativo. Inoltre, non è possibile eseguire il rollback degli inserimenti dalla funzione senza ripristinare altri risultati della transazione esterna, a meno che non si utilizzino i punti di salvataggio. Il corpo della funzione viene sempre eseguito all'interno di una transazione, ma non è una transazione da solo. Non c'è modo di chiamare una funzione che non avvia una transazione. –

1

Il docs dire questo:

un punto di salvataggio è un marchio speciale all'interno di una transazione che consente a tutti i comandi che vengono eseguiti dopo che è stato stabilito per il rollback, ripristinando lo stato delle transazioni per quello che era al momento del punto di salvezza.

Anche loro danno esempi.

Edit:

È necessario avvolgere un transaction in BEGIN e COMMIT comandi.

una transazione è impostato circondando i comandi SQL dell'operazione con BEGIN e COMMIT comandi

+0

Non sono d'accordo sul fatto che i documenti siano chiari. La descrizione di un "punto di salvataggio" suona esattamente quello che so essere una "transazione". I punti di salvataggio sono atomici? – jamieb

+0

A quanto ho capito, sono solo indicatori in una transazione a cui è possibile eseguire il rollback. L'intera transazione è atomica; fino a quando non viene eseguito il commit, nessuna modifica è visibile a nessun'altra transazione. – tom

1

Punti di salvataggio possono essere usati per emulare le transazioni nidificate. Poiché una transazione postgresql è una sequenza di istruzioni che verranno applicate o scartate, i punti di salvataggio possono contrassegnare i punti all'interno di quella sequenza che consentono il ripristino a.

Poiché le transazioni nidificate vere non sono supportate, questa è la soluzione migliore (e una buona soluzione).

+0

Hai provato a usare un punto di salvataggio in una funzione plpgsql? – jamieb

4

Non è possibile utilizzare il comando commit o rollback nella funzione, ma è possibile utilizzare la funzione in una transazione impegnato,

BEGIN TRANSACTION; SELECT do_something(); COMMETTERE;

Questo script SQL si impegna solo se non ci sono eccezioni in do_qualcosa, quindi eseguirà il rollback della transazione della funzione.

Problemi correlati