I am trying to replicate a function from Oracle that has the following piece of code
OPEN pcursor FOR v_sql;
loop
fetch pcursor into
out_rec.XYZ
;
exit when pcursor%NOTFOUND;
INSERT INTO tab1 VALUES (out_rec.XYZ);
COMMIT;
pipe row(out_rec);
end loop;
EXCEPTION
WHEN OTHERS THEN
v_err_code := SQLCODE;
The query stored in v_sql has an issue where for certain specific row values it fails to execute.
I.e. in SQL developer if I execute the query, it returns some data, but if I fetch the resultset to the end or do a count(*) on it, it fails because one of the values in the query returns bad data. The error is irrelevant, but the behavior of this function is that it returns data until it reaches the point of error and then it completes. The data is incomplete (any rows beyond the point of error are not returned) but it is there. This is because of the exception handling clause that doesn’t really do anything (it eats up the exception)
Now when I am trying to replicate this in PostgreSQL, I am facing a couple issues.
- I can’t figure out a way to execute the query lazily. When I execute it – it just fails regardless of whether I fetch all data or not.
- I created the following code to emulate the cursor’s behavior
CREATE OR REPLACE FUNCTION test_fn()
RETURNS table (t ret_data_type)
LANGUAGE plpgsql
AS $function$
...
for t in execute v_sql
loop
return next;
end loop;
exception when others then
raise notice 'error';
...
This runs fine for data with no errors in it, but if there is any error in the entire dataset, it just fails and returns no dataset.
My question is: Is there any way to have Postgresql evaluate the cursor lazily (a few rows at a time) and keep returning data until it encounters the error? I am looking around in the docs but I don’t see anything.
2
Answers
It doesn’t – one of the main points of cursors is not doing exactly that. There isn’t much to add over @Frank Heikens‘ immediate response: put the exception handling inside the loop if you want it to continue.
demo at db-fiddle
Even though there’s an invalid
json
in the middle of the input table, handling/ignoring the exception inside the loop lets you continue afterwards.Handling the
exception
at the end of the whole function will terminate it once you handle or ignore the error. If you moved that inwards, into the loop and added abegin..exception..end
wrapping aroundreturn next
, that still wouldn’t work since the exception’s likely thrown in the assignment above that, infor t in execute v_sql
. You’d have to show your actual code to say for sure.The
when others then
is just for demonstration, it’s better toassert
and handle different exceptions adequately.Based on the great answer of @Zegarek I wrote a small example using assert in an exception block. Never used it before, works really nice, and especially when using the function pg_input_is_valid():