tags:

views:

262

answers:

3

Coming from the MS SQL world, I tend to make heavy use of stored procedures. I'm currently writing an application uses a lot of PostgreSQL plpgsql functions. What I'd like to do is rollback all INSERTS/UPDATES contained within a particular function if I get an exception at any point within it.

I was originally under the impression that each function is wrapped in it's own transaction and that an exception would automatically rollback everything. However, that doesn't seem to be the case. I'm wondering if I ought to be using savepoints in combination with exception handling instead? But I don't really understand the difference between a transaction and a savepoint to know if this is the best approach. Any advice please?

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;
+1  A: 

The docs say this:

A savepoint is a special mark inside a transaction that allows all commands that are executed after it was established to be rolled back, restoring the transaction state to what it was at the time of the savepoint.

They give examples too.

Edit:

You need to wrap a transaction in BEGIN and COMMIT commands.

a transaction is set up by surrounding the SQL commands of the transaction with BEGIN and COMMIT commands

tom
I disagree that the docs are clear. The description of a "savepoint" sounds exactly what I know a "transaction" to be. Are savepoints atomic?
jamieb
As I understand it, they're just markers in a transaction that you can roll back to. The whole transaction is atomic; until it's committed no change is visible to any other transaction.
tom
A: 

Savepoints can be used to emulate nested transactions. Because a postgresql transaction is a sequence of statements that will either be applied or discarded, savepoints can mark points within that sequence that allow rolling back to.

Since true nested transactions are not supported, this is your best bet (and a good one at that).

gahooa
Have you tried to use a savepoint in a plpgsql function?
jamieb
+2  A: 

A function does represent a transaction. You do not have to wrap a function in BEGIN/COMMIT.

Joshua D. Drake
That is not true for PostgreSQL. A single SQL statement, when not executed as a part of explicit transaction (is not executed between BEGIN and COMMIT/END), is executed as a single transaction. But if such statement calls multiple functions, then all the functions are executed in one transaction. Also, when you have multiple statement transaction (explicit BEGIN, COMMIT) and those statements call some procedures, all of those will be executed in the single transactions.As others have said: savepoints are the way to go.
Jacek Konieczny
@Jacek: Joshua is right, a function does represent it's own transaction. You don't need a savepoint to rollback both inserts, if one fails, they both will fail.
Frank Heikens
@Frank: But that is because of the outer transaction. The inserts in other function called in the same statement or previous statements in the transaction will also fail. And you cannot rollback the inserts from the function without rolling back other results of the outer transaction, unless you use the savepoints.Function body is always executed within a transaction, but it is not a transaction by itself. There is just no way to call a function not starting a transaction.
Jacek Konieczny