Skip to content

Commit 4ed9ac4

Browse files
committedJul 18, 2022
Fix #23 - Added new rule G-3220: Always process saved exceptions from a FORALL statement.
1 parent 0eeb731 commit 4ed9ac4

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# G-3220: Always process saved exceptions from a FORALL statement.
2+
3+
!!! warning "Major"
4+
Reliability, Testability
5+
6+
## Reason
7+
8+
Using `save exceptions` in a `forall` statement without actually processing the saved exceptions is just wasted work.
9+
10+
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.
11+
12+
## Example (bad)
13+
14+
``` sql
15+
declare
16+
t_employee_ids employee_api.t_employee_ids_type;
17+
co_increase constant employees.salary%type := 0.1;
18+
co_department_id constant departments.department_id%type := 10;
19+
e_bulk_errors exception;
20+
pragma exception_init(e_bulk_errors,-24381);
21+
begin
22+
t_employee_ids := employee_api.employee_ids_by_department(
23+
id_in => co_department_id
24+
);
25+
<<process_employees>>
26+
forall i in 1..t_employee_ids.count() save exceptions
27+
update employees
28+
set salary = salary + (salary * co_increase)
29+
where employee_id = t_employee_ids(i);
30+
end;
31+
/
32+
```
33+
34+
## Example (good)
35+
36+
``` sql
37+
declare
38+
t_employee_ids employee_api.t_employee_ids_type;
39+
co_increase constant employees.salary%type := 0.1;
40+
co_department_id constant departments.department_id%type := 10;
41+
e_bulk_errors exception;
42+
pragma exception_init(e_bulk_errors,-24381);
43+
begin
44+
t_employee_ids := employee_api.employee_ids_by_department(
45+
id_in => co_department_id
46+
);
47+
<<process_employees>>
48+
forall i in 1..t_employee_ids.count() save exceptions
49+
update employees
50+
set salary = salary + (salary * co_increase)
51+
where employee_id = t_employee_ids(i);
52+
exception
53+
when e_bulk_errors then
54+
<<handle_bulk_exceptions>>
55+
for i in 1..sql%bulk_exceptions.count
56+
loop
57+
logger.log(sql%bulk_exceptions(indx).error_code);
58+
end loop handle_bulk_exceptions;
59+
end;
60+
/
61+
```

‎docs/9-appendix/appendix.md

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ n/a | 3185 | Never use ROWNUM at the same query level as ORDER BY. | Major | |
5858
n/a | 3190 | Avoid using NATURAL JOIN. | Major | &#10008; | | | | &#10008; | | |
5959
n/a | 3195 | Always use wildcards in a LIKE clause. | Minor | | | &#10008; | | | | |
6060
30 | 3210 | Always use BULK OPERATIONS (BULK COLLECT, FORALL) whenever you have to execute a DML statement for more than 4 times. | Major | | &#10008; | | | | | |
61+
n/a | 3220 | Always process saved exceptions from a FORALL statement. | Major | | | | | &#10008; | | | &#10008;
6162
n/a | 3310 | Never commit within a cursor loop. | Critical | | &#10008; | | | &#10008; | | |
6263
n/a | 3320 | Try to move transactions within a non-cursor loop into procedures. | Major | | | &#10008; | | | &#10008; | | &#10008;
6364
31 | 4110 | Always use %NOTFOUND instead of NOT %FOUND to check whether a cursor returned data. | Minor | | | &#10008; | | | | |

0 commit comments

Comments
 (0)