Skip to content

Commit efe7c60

Browse files
committed
Fix #22 - Added Transaction Control category with rules G-3210 and G-3220
1 parent 2a0b3a2 commit efe7c60

File tree

4 files changed

+119
-0
lines changed

4 files changed

+119
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
title: Transaction Control
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# G-3210: Never commit within a cursor loop.
2+
3+
!!! danger "Critical"
4+
Efficiency, Reliability
5+
6+
## Reason
7+
8+
Doing frequent commits within a cursor loop (all types of loops over cursors, whether implicit cursor for loop or loop with explicit fetch from cursor or cursor variable) risks not being able to complete due to ORA-01555, gives bad performance, and risks that the work is left in an unknown half-finished state and cannot be restarted.
9+
10+
* If the work belongs together (an atomic transaction) the commit should be moved to after the loop. Or even better if the logic can be rewritten to a single DML statement on all relevant rows instead of a loop, committing after the single statement.
11+
* If each loop iteration is a self-contained atomic transaction, consider instead to populate a collection of "transactions" to be done (taking restartability into account by collection population), loop over that collection (instead of looping over a cursor) and call a procedure (that contains the transaction logic and the commit) in the loop (see also G-3220).
12+
13+
14+
## Example (bad)
15+
16+
``` sql
17+
declare
18+
l_counter integer := 0;
19+
l_discount discount.percentage%type;
20+
begin
21+
for r_order in (
22+
select o.order_id, o.customer_id
23+
from orders o
24+
where o.order_status = 'New'
25+
) loop
26+
l_discount := sales_api.calculate_discount(p_customer_id => r_order.customer_id);
27+
28+
update order_lines ol
29+
set ol.discount = l_discount
30+
where ol.order_id = r_order.order_id;
31+
32+
l_counter := l_counter + 1;
33+
if l_counter = 100 then
34+
commit;
35+
l_counter := 0;
36+
end if;
37+
end loop;
38+
if l_counter > 0 then
39+
commit;
40+
end if;
41+
end;
42+
/
43+
```
44+
45+
## Example (good)
46+
47+
``` sql
48+
declare
49+
l_discount discount.percentage%type;
50+
begin
51+
for r_order in (
52+
select o.order_id, o.customer_id
53+
from orders o
54+
where o.order_status = 'New'
55+
) loop
56+
l_discount := sales_api.calculate_discount(p_customer_id => r_order.customer_id);
57+
58+
update order_lines ol
59+
set ol.discount = l_discount
60+
where ol.order_id = r_order.order_id;
61+
end loop;
62+
63+
commit;
64+
end;
65+
/
66+
```
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# G-3220: Try to move transactions within a non-cursor loop into procedures.
2+
3+
!!! warning "Major"
4+
Maintainability, Reusability, Testability
5+
6+
## Reason
7+
8+
Commit inside a non-cursor loop (other loop types than loops over cursors - see also G-3210) is either a self-contained atomic transaction, or it is a chunk (with suitable restartability handling) of very large data manipulations. In either case encapsulating the transaction in a procedure is good modularity, enabling reuse and testing of a single call.
9+
10+
## Example (bad)
11+
12+
``` sql
13+
begin
14+
for l_counter in 1..5 loop
15+
insert into headers (id, text) values (l_counter, 'Number '||l_counter);
16+
17+
insert into lines (header_id, line_no, text)
18+
select l_counter, rownum, 'Line '||rownum
19+
from dual
20+
connect by level <= 3;
21+
22+
commit;
23+
end loop;
24+
end;
25+
/
26+
```
27+
28+
## Example (good)
29+
30+
``` sql
31+
declare
32+
procedure create_rows (
33+
p_header_id in headers.id%type
34+
) is
35+
begin
36+
insert into headers (id, text) values (p_header_id, 'Number '||p_header_id);
37+
38+
insert into lines (header_id, line_no, text)
39+
select p_header_id, rownum, 'Line '||rownum
40+
from dual
41+
connect by level <= 3;
42+
43+
commit;
44+
end;
45+
begin
46+
for l_counter in 1..5 loop
47+
create_rows(l_counter);
48+
end loop;
49+
end;
50+
/
51+
```

docs/stylesheets/extra.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
[id="large-objects"],
9090
[id="dml-sql"],
9191
[id="bulk-operations"],
92+
[id="transaction-control"],
9293
[id="control-structures"],
9394
[id="case-if-decode-nvl-nvl2-coalesce"],
9495
[id="flow-control"],

0 commit comments

Comments
 (0)