Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005

rekharaghuram
from rekharaghuram More from this publisher
05.11.2015 Views

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:

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).

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!