Here's below my procedure. Inside u find a loop. Target is if any of single loop execution will throw exception i don't want to stop the process and want to continue and commit each loop query which was executed succesfully. Therefore i placed exception catch inside loop. As u can see i also got a commit at the end and some begin/end blocks. My question is whether did i correctly or maybe i should put additional commit inside begin/end inside the loop (just after execute mysql;)? Thank you in advance.
CREATE OR REPLACE PROCEDURE myProc()
LANGUAGE plpgsql
AS $procedure$
declare
mysql text;
tb_name text;
myTables CURSOR for
SELECT table_name
FROM information_schema.tables
WHERE table_type='BASE TABLE'
AND table_schema='dist';
begin
begin
call DoSomeJob();
for tb in myTables loop
tb_name := tb;
begin
mysql := format('delete from %I where somecol=2', tb_name);
execute mysql;
exception
when others then
raise notice '% %', SQLERRM, SQLSTATE;
end ;
end loop;
call doOtherJob();
exception
when others then
raise notice 'The transaction is in an uncommittable state. '
'Transaction was rolled back';
raise notice '%: %', SQLSTATE, sqlerrm;
end ;
commit;
end;
$procedure$
;
UPDATE:
CREATE OR REPLACE PROCEDURE myProc()
LANGUAGE plpgsql
AS $procedure$
declare
mysql text;
tb_name text;
myTables CURSOR for
SELECT table_name
FROM information_schema.tables
WHERE table_type='BASE TABLE'
AND table_schema='dist';
begin
begin
call DoSomeJob();
exception
when others then
raise notice 'The transaction is in an uncommittable state. '
'Transaction was rolled back';
raise notice '%: %', SQLSTATE, sqlerrm;
end;
RAISE EXCEPTION 'ERROR test';
for tb in myTables loop
tb_name := tb;
begin
mysql := format('delete from %I where somecol=2', tb_name);
execute mysql;
exception
when others then
raise notice '% %', SQLERRM, SQLSTATE;
end ;
end loop;
begin
call doOtherJob();
exception
when others then
raise notice 'The transaction is in an uncommittable state. '
'Transaction was rolled back';
raise notice '%: %', SQLSTATE, sqlerrm;
end;
commit;
end;
$procedure$;
CodePudding user response:
I explain you how to works PostgreSQL transactions in procedures. Firstly
- begin is a - "begin transaction";
- end is a - "commit transaction"
And when you using transaction in another transaction (sub transactions) when your first level transaction rollbacked then all sub transactions also will be rollbacked. For example:
begin --block call2
begin --block call1
call1;
exception
when others then
raise notice 'error call1'
end;
call2;
exception
when others then
raise notice 'error call2'
end;
Here when call2 failed then call1 also be failed. Because block call1 is in the block call2
And on your procedure - when doOtherJob() will be fail then all your inserted data will fail to. For solving this problem you can write your procedure as below:
CREATE OR REPLACE PROCEDURE myProc()
LANGUAGE plpgsql
AS $procedure$
declare
mysql text;
tb_name text;
myTables CURSOR for
SELECT table_name
FROM information_schema.tables
WHERE table_type='BASE TABLE'
AND table_schema='dist';
begin
call DoSomeJob();
for tb in myTables loop
tb_name := tb;
begin
mysql := format('delete from %I where somecol=2', tb_name);
execute mysql;
exception
when others then
raise notice '% %', SQLERRM, SQLSTATE;
end ;
end loop;
begin
call doOtherJob();
exception
when others then
raise notice 'The transaction is in an uncommittable state. '
'Transaction was rolled back';
raise notice '%: %', SQLSTATE, sqlerrm;
end;
commit;
end;
$procedure$;
You can set for block DoSomeJob() to the same logic.
CodePudding user response:
When using procedures in PostgreSQL, especially about transactions, you need to know and understand some details well. I will now explain to you the important details and you will understand how it works.
begin - this is start transaction (always)
exception
end - this is not always commit-transaction, because if exception is created then end will be rollback-transaction if else end will be commit-transaction
And remember that: each block end affects only its own block begin
At the same time, pay attention to this:
During rollback process, all other begin-exception-end blocks which is under between the begin block and the exception block will be rollback.
Please execute this block and view result: select * from table test1
DO
$body$
begin
CREATE TABLE test1 (
id int4 NOT NULL,
CONSTRAINT test1_pk PRIMARY KEY (id)
);
begin -- block 1
insert into test1 (id) values (1);
exception
when others then
raise notice 'exception ';
end;
begin -- block 2
insert into test1 (id) values (error);
exception
when others then
raise notice 'exception ';
end;
begin -- block 3
insert into test1 (id) values (3);
exception
when others then
raise notice 'exception ';
end;
begin --block 4
begin -- block 5
insert into test1 (id) values (5);
exception
when others then
raise notice 'exception ';
end;
insert into test1 (id) values (error);
exception
when others then
raise notice 'exception ';
end;
END;
$body$
LANGUAGE 'plpgsql';
block 1- success execute (success insert 1) - commitblock 2- execute with error (no inserted record) - rollbackblock 3- execute success (success insert 3) - commitblock 5- execute success (success insert 5) - commitblock 4- execute with error (no inserted record) - rollback
Since block 5 is inside block 4 so block 5 already rollbacked
Result: select * from test1
1
3
