G-3220: Always process saved exceptions from a FORALL statement.
Critical
Reliability, Testability
Reason
Using save exceptions in a forall statement without actually processing the saved exceptions is just wasted work.
If your use of forall is meant to be atomic (all or nothing), don't use save exceptions. If you want to handle errors of individual rows and do use save exceptions, always include an exception handler block with a loop to process the saved exceptions.
Example (bad)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | declare
t_employee_ids employee_api.t_employee_ids_type;
co_increase constant employees.salary%type := 0.1;
co_department_id constant departments.department_id%type := 10;
e_bulk_errors exception;
pragma exception_init(e_bulk_errors,-24381);
begin
t_employee_ids := employee_api.employee_ids_by_department(
id_in => co_department_id
);
<<process_employees>>
forall i in 1..t_employee_ids.count() save exceptions
update employees
set salary = salary + (salary * co_increase)
where employee_id = t_employee_ids(i);
end;
/
|
Example (good)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | declare
t_employee_ids employee_api.t_employee_ids_type;
co_increase constant employees.salary%type := 0.1;
co_department_id constant departments.department_id%type := 10;
e_bulk_errors exception;
pragma exception_init(e_bulk_errors,-24381);
begin
t_employee_ids := employee_api.employee_ids_by_department(
id_in => co_department_id
);
<<process_employees>>
forall i in 1..t_employee_ids.count() save exceptions
update employees
set salary = salary + (salary * co_increase)
where employee_id = t_employee_ids(i);
exception
when e_bulk_errors then
<<handle_bulk_exceptions>>
for i in 1..sql%bulk_exceptions.count
loop
logger.log(sql%bulk_exceptions(i).error_code);
end loop handle_bulk_exceptions;
end;
/
|