Could I use a second function to run the first one on every row of the table and rollback every insert if any one row results with an error message
A FUNCTION
always runs in a single transaction. But I don't think that stands in the way of what you are trying to do. A block with an EXCEPTION
clause can trap exceptions and roll back subtransactions. Depending on what "every insert" is supposed to mean exactly, that might work for you.
If "every row" means every row in the whole operation (multiple calls of the child function), then you don't need to do anything extra. It's default behavior to roll back everything if an exception occurs.
If "every row" means every row affected by a single call of the child function, then consider the manual on Trapping Errors:
When an error is caught by an EXCEPTION
clause, the local variables
of the PL/pgSQL function remain as they were when the error occurred,
but all changes to persistent database state within the block are
rolled back.
Bold emphasis mine. Related:
To actually COMMIT
work at any point in the process, you need a PROCEDURE
, introduced with Postgres 11. Starting with the same version, transaction control statements are also allowed in anonymous code blocks executed with the DO
command.
Basically:
DO
$do$
DECLARE
_rec "UsagePs";
BEGIN
FOR _rec IN
SELECT * FROM "UsagePs" u WHERE u.foo = 'bar' ORDER BY u.id
LOOP
BEGIN
PERFORM public.process_single_usage(_rec);
-- COMMIT; -- ? -- requires Postgres 11
EXCEPTION
WHEN OTHERS THEN
RAISE WARNING 'This error happened: %!', SQLERRM;
END;
END LOOP;
END
$do$;
I don't think you even need or want to COMMIT
inside the loop. You just want to roll back subtransactions - or everything.
Note the use of SQLERRM
to propagate any error message from the child function out, downgraded to WARNING
. The manual:
The special variable SQLERRM
contains the error message associated with the exception.
Related: