Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
CHAPTER 8 ■ TRANSACTIONS 279 5 pragma autonomous_transaction; 6 l_cnt number; 7 begin 8 9 select count(*) into l_cnt 10 from dual 11 where EXISTS ( select null 12 from emp 13 where empno = :new.empno 14 start with mgr = ( select empno 15 from emp 16 where ename = USER ) 17 connect by prior empno = mgr ); 18 if ( l_cnt = 0 ) 19 then 20 insert into audit_tab ( msg ) 21 values ( 'Attempt to update ' || :new.empno ); 22 commit; 23 24 raise_application_error( -20001, 'Access Denied' ); 25 end if; 26 end; 27 / Trigger created. Note the use of the CONNECT BY query. This will resolve the entire hierarchy for us, based on the current user. It will verify that the record we are attempting to update belongs to someone who reports to us at some level. The main points to note about the action of this trigger are as follows: • PRAGMA AUTONOMOUS_TRANSACTION is applied to the trigger definition. This entire trigger is an “autonomous transaction” and so it is independent of the parent transaction, the attempted update. • The trigger actually reads from the table it is protecting, the EMP table, in the query. That in itself would lead to a mutating table error at runtime were it not for the fact that this is an autonomous transaction. The autonomous transaction gets us around this problem—it allows us to read the table, but with the downside being that we’re not able to see the changes we ourselves have made to the table. Extreme caution must be exercised in such a case. This logic must be carefully inspected. What if the transaction we were performing was an update to the employee hierarchy itself? We would not see those changes in the trigger, and this must be taken into consideration when evaluating the correctness of this trigger. • This trigger commits. This has never been possible before—triggers could never commit work. This trigger is not committing the work that actually fired the trigger; rather, it is committing only the work that the trigger has performed (the audit record).
280 CHAPTER 8 ■ TRANSACTIONS So, we have set up the EMP table that has a nice hierarchical structure (EMPNO, MGR recursive relationship). We also have an AUDIT_TAB table into which we want to record failed attempts to modify information. We have a trigger to enforce our rule that only our manager or our manager’s manager (and so on) may modify our record. Let’s see how this works by trying to update a record in the EMP table: ops$tkyte@ORA10G> update emp set sal = sal*10; update emp set sal = sal*10 * ERROR at line 1: ORA-20001: Access Denied ORA-06512: at "OPS$TKYTE.EMP_AUDIT", line 21 ORA-04088: error during execution of trigger 'OPS$TKYTE.EMP_AUDIT' ops$tkyte@ORA10G> select * from audit_tab; USERNAME TIMESTAMP MSG --------- --------- ---------------------------------------- OPS$TKYTE 27-APR-05 Attempt to update 7369 The trigger caught us and was able to prevent the UPDATE from occurring, while at the same time creating a permanent record of the attempt (notice how it used the DEFAULT keyword on the CREATE TABLE statement for the AUDIT_TAB table to automatically have the USER and SYSDATE values inserted for us). Next, let’s log in as a user who can actually do an UPDATE and try some things out: ops$tkyte@ORA10G> connect scott/tiger Connected. scott@ORA10G> set echo on scott@ORA10G> update ops$tkyte.emp set sal = sal*1.05 where ename = 'ADAMS'; 1 row updated. scott@ORA10G> update ops$tkyte.emp set sal = sal*1.05 where ename = 'SCOTT'; update ops$tkyte.emp set sal = sal*1.05 where ename = 'SCOTT' * ERROR at line 1: ORA-20001: Access Denied ORA-06512: at "OPS$TKYTE.EMP_AUDIT", line 21 ORA-04088: error during execution of trigger 'OPS$TKYTE.EMP_AUDIT' In the default install of the demonstration table EMP, the employee ADAMS works for SCOTT, so the first UPDATE succeeds. The second UPDATE, where SCOTT tries to give himself a raise, fails since SCOTT does not report to SCOTT. Logging back into the schema that holds the AUDIT_TAB table, we see the following:
- Page 274 and 275: CHAPTER 6 ■ LOCKING AND LATCHING
- Page 276 and 277: CHAPTER 7 ■ ■ ■ Concurrency a
- Page 278 and 279: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 280 and 281: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 282 and 283: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 284 and 285: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 286 and 287: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 288 and 289: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 290 and 291: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 292 and 293: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 294 and 295: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 296 and 297: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 298 and 299: CHAPTER 7 ■ CONCURRENCY AND MULTI
- Page 300 and 301: CHAPTER 8 ■ ■ ■ Transactions
- Page 302 and 303: CHAPTER 8 ■ TRANSACTIONS 257 •
- Page 304 and 305: CHAPTER 8 ■ TRANSACTIONS 259 So,
- Page 306 and 307: CHAPTER 8 ■ TRANSACTIONS 261 X --
- Page 308 and 309: CHAPTER 8 ■ TRANSACTIONS 263 “s
- Page 310 and 311: CHAPTER 8 ■ TRANSACTIONS 265 busi
- Page 312 and 313: CHAPTER 8 ■ TRANSACTIONS 267 Many
- Page 314 and 315: CHAPTER 8 ■ TRANSACTIONS 269 ops$
- Page 316 and 317: CHAPTER 8 ■ TRANSACTIONS 271 last
- Page 318 and 319: CHAPTER 8 ■ TRANSACTIONS 273 Dist
- Page 320 and 321: CHAPTER 8 ■ TRANSACTIONS 275 Auto
- Page 322 and 323: CHAPTER 8 ■ TRANSACTIONS 277 3 Au
- Page 326: CHAPTER 8 ■ TRANSACTIONS 281 scot
- Page 329 and 330: 284 CHAPTER 9 ■ REDO AND UNDO cri
- Page 331 and 332: 286 CHAPTER 9 ■ REDO AND UNDO Fir
- Page 333 and 334: 288 CHAPTER 9 ■ REDO AND UNDO The
- Page 335 and 336: 290 CHAPTER 9 ■ REDO AND UNDO We
- Page 337 and 338: 292 CHAPTER 9 ■ REDO AND UNDO Wha
- Page 339 and 340: 294 CHAPTER 9 ■ REDO AND UNDO row
- Page 341 and 342: 296 CHAPTER 9 ■ REDO AND UNDO If
- Page 343 and 344: 298 CHAPTER 9 ■ REDO AND UNDO ops
- Page 345 and 346: 300 CHAPTER 9 ■ REDO AND UNDO Inv
- Page 347 and 348: 302 CHAPTER 9 ■ REDO AND UNDO The
- Page 349 and 350: 304 CHAPTER 9 ■ REDO AND UNDO 41
- Page 351 and 352: 306 CHAPTER 9 ■ REDO AND UNDO ins
- Page 353 and 354: 308 CHAPTER 9 ■ REDO AND UNDO So,
- Page 355 and 356: 310 CHAPTER 9 ■ REDO AND UNDO ops
- Page 357 and 358: 312 CHAPTER 9 ■ REDO AND UNDO ops
- Page 359 and 360: 314 CHAPTER 9 ■ REDO AND UNDO •
- Page 361 and 362: 316 CHAPTER 9 ■ REDO AND UNDO ...
- Page 363 and 364: 318 CHAPTER 9 ■ REDO AND UNDO •
- Page 365 and 366: 320 CHAPTER 9 ■ REDO AND UNDO bac
- Page 367 and 368: 322 CHAPTER 9 ■ REDO AND UNDO As
- Page 369 and 370: 324 CHAPTER 9 ■ REDO AND UNDO ops
- Page 371 and 372: 326 CHAPTER 9 ■ REDO AND UNDO wil
- Page 373 and 374: 328 CHAPTER 9 ■ REDO AND UNDO Thi
CHAPTER 8 ■ TRANSACTIONS 279<br />
5 pragma autonomous_transaction;<br />
6 l_cnt number;<br />
7 begin<br />
8<br />
9 select count(*) into l_cnt<br />
10 from dual<br />
11 where EXISTS ( select null<br />
12 from emp<br />
13 where empno = :new.empno<br />
14 start with mgr = ( select empno<br />
15 from emp<br />
16 where ename = USER )<br />
17 connect by prior empno = mgr );<br />
18 if ( l_cnt = 0 )<br />
19 then<br />
20 insert into audit_tab ( msg )<br />
21 values ( 'Attempt to update ' || :new.empno );<br />
22 commit;<br />
23<br />
24 raise_application_error( -20001, 'Access Denied' );<br />
25 end if;<br />
26 end;<br />
27 /<br />
Trigger created.<br />
Note the use of the CONNECT BY query. This will resolve the entire hierarchy for us, based<br />
on the current user. It will verify that the record we are attempting to update belongs to someone<br />
who reports to us at some level.<br />
The main points to note about the action of this trigger are as follows:<br />
• PRAGMA AUTONOMOUS_TRANSACTION is applied to the trigger definition. This entire trigger<br />
is an “autonomous transaction” <strong>and</strong> so it is independent of the parent transaction, the<br />
attempted update.<br />
• The trigger actually reads from the table it is protecting, the EMP table, in the query.<br />
That in itself would lead to a mutating table error at runtime were it not for the fact that<br />
this is an autonomous transaction. The autonomous transaction gets us around this<br />
problem—it allows us to read the table, but with the downside being that we’re not able<br />
to see the changes we ourselves have made to the table. Extreme caution must be exercised<br />
in such a case. This logic must be carefully inspected. What if the transaction we<br />
were performing was an update to the employee hierarchy itself? We would not see<br />
those changes in the trigger, <strong>and</strong> this must be taken into consideration when evaluating<br />
the correctness of this trigger.<br />
• This trigger commits. This has never been possible before—triggers could never commit<br />
work. This trigger is not committing the work that actually fired the trigger; rather,<br />
it is committing only the work that the trigger has performed (the audit record).