ROLLBACK in a function - Mailing list pgsql-general

From Ken Winter
Subject ROLLBACK in a function
Date
Msg-id C40884B22A0E4AAC8278E471EF0AE368@KenIBM
Whole thread Raw
Responses Re: ROLLBACK in a function  (Craig Ringer <craig@postnewspapers.com.au>)
List pgsql-general

How can I write a PL/PgSQL function that rolls back every database change it has done?

 

I’m about to write a set of database test functions.  Each function needs to do some INSERT, UPDATE, or DELETE actions, test whether they had their intended effect, and then roll back the test changes to restore the database to its prior state.  Here’s a simple example:

 

CREATE OR REPLACE FUNCTION tests.person() RETURNS varchar AS

$BODY$

DECLARE

          errs VARCHAR = '';

          -- Test declarations:

          old_count BIGINT;

          new_count BIGINT;

BEGIN

--       RAISE EXCEPTION '';

-- EXCEPTION

--       WHEN raise_exception THEN

          SAVEPOINT s;

          SET search_path TO public, tests;

          -- Test code:

                   SELECT count(*) INTO old_count FROM person_i;

                   INSERT INTO person (last_name) VALUES ('_test');

                   SELECT count(*) INTO new_count FROM person_i;

                   IF NOT new_count = old_count + 1 THEN

                             errs := errs || ': Failed to insert into ''person'';

                   END IF;

          ROLLBACK TO SAVEPOINT s;

          RETURN errs;

END;

$BODY$

LANGUAGE plpgsql VOLATILE

;

When I run this function, I get this error:

 

ERROR: SPI_execute_plan failed executing query "SAVEPOINT s": SPI_ERROR_TRANSACTION

SQL state: XX000

Context: PL/pgSQL function "person" line 11 at SQL statement

 

I tried another way to make the rollback happen: The last paragraph of http://www.postgresql.org/docs/8.3/static/plpgsql-structure.html said “a block containing an EXCEPTION clause effectively forms a subtransaction that can be rolled back without affecting the outer transaction.”  So I rewrote the test function like this:

 

CREATE OR REPLACE FUNCTION tests.person() RETURNS varchar AS

$BODY$

DECLARE

          errs VARCHAR = '';

          -- Test declarations:

          old_count BIGINT;

          new_count BIGINT;

BEGIN

          RAISE EXCEPTION '';

EXCEPTION

          WHEN raise_exception THEN

                   SET search_path TO public, tests;

                   -- Test code:

                             SELECT count(*) INTO old_count FROM person_i;

                             INSERT INTO person (last_name) VALUES ('_test');

                             SELECT count(*) INTO new_count FROM person_i;

                             IF NOT new_count = old_count + 1 THEN

                                      errs := errs || ': Failed to insert into ''person''’;

                             END IF;

                   RETURN errs;

END;

$BODY$

LANGUAGE plpgsql VOLATILE

;

 

The function runs OK, but it does not roll back the actions it did (in this case, the INSERT).

 

~ TIA

~ Ken

 

 

pgsql-general by date:

Previous
From: rihad
Date:
Subject: Re: UPDATE ... RETURNING atomicity
Next
From: Grzegorz Jaśkiewicz
Date:
Subject: Re: UPDATE ... RETURNING atomicity