postgres stored procedure pl pgSQL transaction managament - ghdrako/doc_snipets GitHub Wiki

Use CALL to execute the stored procedure.

If CALL is executed in a transaction block, then the called procedure cannot execute transaction control statements. Czyli nie moze zawierac commit-a.

Transaction control is only possible in CALL or DO invocations from the top level or nested CALL or DO invocations without any other intervening command. For example, if the call stack is CALL proc1() → CALL proc2() → CALL proc3(), then the second and third procedures can perform transaction control actions. But if the call stack is CALL proc1() → SELECT func2() → CALL proc3(), then the last procedure cannot do transaction control, because of the SELECT in between.

call proc_with_commit();   -- ok
begin;  -- synonim start transaction
call proc_with_commit();  -- error - not supported nested transaction
end;    -- synonnym commit 
begin;  -- synonim start transaction
call proc_without_commit();  -- ok
end;    -- synonnym commit 

Transaction control

CREATE PROCEDURE proc2()
LANGUAGE plpgsql
AS $$
BEGIN
   FOR idx IN 1..100 LOOP
       INSERT INTO my_tbl(col_num) VALUES(idx);
       IF idx % 10 = 0 THEN A
            COMMIT; B
        ELSE
            ROLLBACK; C 
        END IF;
    END LOOP;
END
$$;

Call (nest) another stored procedure in a stored procedure.

CREATE PROCEDURE proc3() LANGUAGE plpgsql AS $$
BEGIN
INSERT INTO my_tbl VALUES(1); A 
 CALL proc4();
INSERT INTO my_tbl VALUES(4); D 
 COMMIT;
END;
$$;
CREATE PROCEDURE proc4() LANGUAGE plpgsql AS $$
BEGIN
INSERT INTO my_tbl VALUES(2); B 
 ROLLBACK;
INSERT INTO my_tbl VALUES(3); C 
END;
$$;

obraz

Restrictions on transaction control

COMMIT/ROLLBACK cannot be written between BEGIN and EXCEPTION. Instead, use EXCEPTION to make necessary processing into a subblock from BEGIN to END, and write COMMIT/ROLLBACK outside the subblock. obraz

Return cursor from prcedure

There are two kinds of CURSORs: WITH HOLD outlive transactions and WITHOUT HOLD (the default) don't. You may point a refcursor variable to a WITH HOLD cursor in plpgsql with a bit of dynamic SQL. Here's a modified version of your procedure doing that:

CREATE OR REPLACE PROCEDURE public.build_and_populate(INOUT cresults refcursor)
 LANGUAGE plpgsql
AS $procedure$
    BEGIN
        DROP TABLE IF EXISTS procsampledata;
        CREATE TABLE procsampledata as select x,1 as c2,2 as c3, md5(random()::text) from generate_series(1,10) x;
        COMMIT;
        EXECUTE 'DECLARE ' || quote_ident(cresults::text) || ' CURSOR WITH HOLD FOR SELECT * FROM procsampledata';                                               
    END;
$procedure$

postgres=# call BUILD_AND_POPULATE('res');
 cresults 
----------
 res
(1 row)

postgres=# fetch all from res;
 x  | c2 | c3 |               md5                
----+----+----+----------------------------------
  1 |  1 |  2 | 11a3d2e637332a25118c2f4d5dac49c0
  2 |  1 |  2 | 4c5b9cb5ec79479a9daa9ae7a131a078
  3 |  1 |  2 | 320b49912e94de90c3370836706c5494
  4 |  1 |  2 | a9ccf45dbbbbec19d18b69cf4a0fc26b
  5 |  1 |  2 | 1a52801f1761cb0e357a4468803473e1
  6 |  1 |  2 | 96c395dad11ee19526bc08b5ad114905
  7 |  1 |  2 | 7d0da9a0cf2520ee871185677d7062cf
  8 |  1 |  2 | 65ecf1e68806a0b9133fa0e47056574e
  9 |  1 |  2 | bae5c688e1ef6b9c389b37ecc7a18342
 10 |  1 |  2 | ed07142f3b51a282c05370890a8af468
(10 rows)

postgres=# close res;
CLOSE CURSOR