COMPOUND TRIGGER RESOLUTION
Priya, November 21, 2016 - 8:28 pm UTC
Awsome solution. It works perfect with small change.
I got below error..When I call,procedure delete_XXXX in "AFTER STATEMENT"
ORA-04084: cannot change NEW values for this trigger type
after statement is
36 begin
37 if deleting then
38 audit_pkg.delete_separate_transaction(v_audit);
39 raise_application_error (-20002, 'dblogin_user_info table data can not be deleted', true);
40 end if;
Later, I put the above code in "BEFORE EACH ROW" then it works..
before each row is
9 begin
10 if deleting then
11 v_audit(v_audit.count+1).action := 'delete';
12 v_audit(v_audit.count).who := user;
13 v_audit(v_audit.count).dte := sysdate;
14 v_audit(v_audit.count).x := :old.x;
15 v_audit(v_audit.count).y := :old.y;
audit_pkg.delete_separate_transaction(v_audit);
39 raise_application_error (-20002, 'dblogin_user_info table data can not be deleted', true);
16 end if; --close if for deleting
17
As expected, I got this message plus capturing entry in audit table..Plz suggest can I follow the same ?
ORA-20002: dblogin_user_info table data can not be deleted
ORA-06512: at "DB_ADMIN.CMP_TRIG_1", line 24
ORA-04088: error during execution of trigger 'DB_ADMIN.CMP_TRIG_1'
November 22, 2016 - 1:12 am UTC
We need to see your entire code, because
after statement is
36 begin
37 if deleting then
38 audit_pkg.delete_separate_transaction(v_audit);
39 raise_application_error (-20002, 'dblogin_user_info table data can not be deleted', true);
40 end if;
does not look to be changing any :new values to me
Follow up of Compound Trigger
Priya, November 22, 2016 - 4:54 pm UTC
I executed the same code as below. Experienced issues..
SQL> CREATE OR REPLACE package DB_ADMIN.TRI_pkg is
2 type t_dblogin is table of t1%rowtype index by pls_integer;
3 type t_audit is table of audit_tab%rowtype index by pls_integer;
4
5 fire_on_insert boolean := true;
6
7 procedure delete_separate_transaction(p_audit t_audit);
8 end;
9 /
Package created.
SQL> CREATE OR REPLACE package body DB_ADMIN.TRI_pkg is
2 procedure delete_separate_transaction(p_audit t_audit) is
3 pragma autonomous_transaction;
4 begin
5
6 forall j in 1..p_audit.count()
7 insert into audit_tab values p_audit(j);
8 commit;
9 end;
10 end;
11 /
Package body created.
SQL> drop table t1 purge;
SQL> /
Table dropped.
SQL> create table t1 ( x int ,y int, created date, created_by varchar2(10));
Table created.
SQL> drop table audit_tab purge;
Table dropped.
SQL> create table audit_tab ( action varchar2(10), who varchar2(20), dte date, x
int ,y int);
Table created.
SQL>
SQL> create or replace trigger trig_AUDIT
2 for delete or insert or update on t1
3 compound trigger
4
5 v_dblogon TRI_pkg.t_dblogin;
6 v_audit TRI_pkg.t_audit;
7
8 before each row is
9 begin
10 if deleting then
11 v_audit(v_audit.count+1).action := 'delete';
12 v_audit(v_audit.count).who := user;
13 v_audit(v_audit.count).dte := sysdate;
14 v_audit(v_audit.count).x := :old.x;
15 v_audit(v_audit.count).y := :old.y;
16 end if; --close if for deleting
17
18 if inserting then
19 if TRI_pkg.fire_on_insert then
20 :new.created :=sysdate;
21 :new.created_by := user;
22 end if;
23 end if; --close if for inserting
24
25 if updating then
26 v_dblogon(v_dblogon.count+1).x := :old.x;
27 v_dblogon(v_dblogon.count).y := :old.x;
28 v_dblogon(v_dblogon.count).created := :old.created;
29 v_dblogon(v_dblogon.count).created_by := :old.created_by;
30 end if;
31
32 end before each row;
33
34
35 after statement is
36 begin
37 if deleting then
38 TRI_pkg.delete_separate_transaction(v_audit);
39 raise_application_error (-20002, 'dblogin_user_info table data can n
ot be deleted', true);
40 end if;
41
42 if updating then
43 TRI_pkg.fire_on_insert := false;
44 forall i in 1..v_dblogon.count()
45 insert into t1 values v_dblogon(i);
46 TRI_pkg.fire_on_insert := true;
47 end if;
48 end after statement;
49 end trig_AUDIT;
50 /
Trigger created.
SQL>
SQL> INSERT INTO T1(X,Y) VALUES(10,77);
1 row created.
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY
---------- ---------- --------- ----------
10 77 22-NOV-16 DB_ADMIN
SQL> SELECT * FROM AUDIT_TAB;
no rows selected
SQL>
SQL> UPDATE T1 SET X=12;
1 row updated.
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY
---------- ---------- --------- ----------
12 77
10 10
SQL> 10 10
SQL> DELETE FROM T1;
DELETE FROM T1
*
ERROR at line 1:
ORA-04084: cannot change NEW values for this trigger type
ORA-06512: at "DB_ADMIN.TRIG_AUDIT", line 6
ORA-04088: error during execution of trigger 'DB_ADMIN.TRIG_AUDIT'
SQL> SELECT * FROM AUDIT_TAB;
no rows selected
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY
---------- ---------- --------- ----------
12 77
10 10
SQL>
Issue #1: When I delete T1 table, throws an error.
Issue #2: When I update T1 table, it is not adding previous values of CREATED, CREATED_BY for
newly inserted row nor carrying it for old row.
Appreciate your help.
November 23, 2016 - 5:16 am UTC
I think you might have a bug in 11.1.0.6 there - I just tested this in 11.2.0.4 and 12.1.0.2 and the results are as expected.
Follow up of Compound Trigger
Priya, November 23, 2016 - 2:40 pm UTC
This is my Oracle version SQL*Plus: Release 11.1.0.6.0 - Please suggest any possible ways which are suitable for my Oracle DB version.
I'm worried about the effort we spent for this.
November 24, 2016 - 4:26 am UTC
Suggestion 1)
Dont use outdated or unsupported versions of software
Suggestion 2)
Dont use a compound trigger then. Break it up into 3 triggers in the conventional sense. Use a package to hold the state.
Some pseudo-code below to get you started
create or replace
package audit_pkg is
type t_dblogin is table of t1%rowtype index by pls_integer;
type t_audit is table of audit_tab%rowtype index by pls_integer;
v_dblogon audit_pkg.t_dblogin;
v_audit audit_pkg.t_audit;
fire_on_insert boolean := true;
procedure delete_separate_transaction(p_audit t_audit);
end;
/
create or replace
package body audit_pkg is
procedure delete_separate_transaction(p_audit t_audit) is
pragma autonomous_transaction;
begin
forall j in 1..p_audit.count()
insert into audit_tab values p_audit(j);
commit;
end;
end;
/
create or replace trigger trig1
before delete or insert or update on t1
begin
audit_pkg.v_dblogon.delete;
audit_pkg.v_audit.delete;
end;
/
create or replace trigger trig1
before delete or insert or update on t1
for each row
begin
if deleting then
audit_pkg.v_audit(audit_pkg.v_audit.count+1).action := 'delete';
audit_pkg.v_audit(audit_pkg.v_audit.count).who := user;
audit_pkg.v_audit(audit_pkg.v_audit.count).dte := sysdate;
audit_pkg.v_audit(audit_pkg.v_audit.count).x := :old.x;
audit_pkg.v_audit(audit_pkg.v_audit.count).y := :old.y;
end if; --close if for deleting
if inserting then
if audit_pkg.audit_pkg.fire_on_insert then
:new.created :=sysdate;
:new.created_by := user;
end if;
end if; --close if for inserting
if updating then
audit_pkg.v_dblogon(audit_pkg.v_dblogon.count+1).x := :old.x;
audit_pkg.v_dblogon(audit_pkg.v_dblogon.count).y := :old.x;
audit_pkg.v_dblogon(audit_pkg.v_dblogon.count).created := :old.created;
audit_pkg.v_dblogon(audit_pkg.v_dblogon.count).created_by := :old.created_by;
end if;
end;
/
create or replace trigger trig1
after delete or insert or update on t1
begin
if deleting then
audit_pkg.delete_separate_transaction(v_audit);
raise_application_error (-20002, 'dblogin_user_info table data can not be deleted', true);
end if;
if updating then
audit_pkg.fire_on_insert := false;
forall i in 1..audit_pkg.v_dblogon.count()
insert into t1 values audit_pkg.v_dblogon(i);
audit_pkg.fire_on_insert := true;
end if;
end;
/
Follow up of Compound Trigger
Priya, December 01, 2016 - 7:15 pm UTC
Thank you Tom, for such a useful solution.
It works perfects for DELETE, INSERT and UPDATE. But, a small issue with UPDATE is,
1. When a table has a single record , its getting updated and inserting with previous values as a new row. Expected this.
2. When a table has multiple rows, when I update a particular row. It's getting update and inserting with previous values as a new row and also, new records are inserting for all other rows in the table, which I do not.
Please suggest any?
Here is the pseudo code,
BEFORE TRIGGER
UPDATE ON T1
BEGIN
audit_pkg.v_login(audit_pkg.v_login.count+1).ID := :old.ID;
audit_pkg.v_login(audit_pkg.v_oslogin.count).CREATED :=:old.CREATED;
END;
After TRIGGER
UPDATE ON T1
BEGIN
AUDIT_PKG.fire_on_insert := FALSE;
forall i in 1..audit_pkg.v_login.count()
INSERT INTO T1 values audit_pkg.v_login(i);
AUDIT_PKG.fire_on_insert := TRUE;
END;
December 02, 2016 - 2:37 am UTC
give us a full test case showing what you are getting and what you want
Follow up of Compound Trigger
Priya, December 02, 2016 - 5:33 pm UTC
SQL> CREATE OR REPLACE package DB_ADMIN.TRI_pkg is
2 type t_dblogin is table of t1%rowtype index by pls_integer;
3 type t_audit is table of audit_tab%rowtype index by pls_integer;
4
5 v_dblogon tri_pkg.T_DBLOGIN;
6 v_audit tri_pkg.t_audit;
7 fire_on_insert boolean := true;
8
9 procedure delete_transaction(p_audit t_audit);
10 end;
11 /
Package created.
SQL>
SQL> CREATE OR REPLACE package body DB_ADMIN.TRI_pkg is
2 procedure delete_transaction(p_audit t_audit) is
3 pragma autonomous_transaction;
4 begin
5
6 INSERT INTO AUDIT_TAB(ACTION,WHO,DTE) VALUES('DELETE','SCOTT',SYSDATE);
7 COMMIT;
8
9 end;
10 end;
11 /
Package body created.
SQL> CREATE OR REPLACE TRIGGER DB_ADMIN.BEFORE_DML_T1
2 BEFORE DELETE OR INSERT OR UPDATE on T1
3 FOR EACH ROW
4
5 DECLARE
6 V_LOGIN_USER NUMBER;
7
8 BEGIN
9 IF DELETING THEN
10 tri_pkg.delete_transaction( tri_pkg.v_audit);
11 raise_application_error (-20002, 'data can not be delet
ed', true);
12 END IF; --CLOSE IF FOR DELETING
13
14 IF INSERTING THEN
15 if audit_pkg.fire_on_insert then
16 :NEW.CREATED := SYSDATE;
17 :NEW.CREATED_BY := USER ;
18 END IF;
19 END IF; -- CLOSE IF FOR INSERTING
20
21 IF UPDATING THEN
22 IF UPPER(:OLD.status) = 'FALSE' THEN
23 RAISE_APPLICATION_ERROR (-20002, 'FALSE RECORDS C
AN NOT BE UPDATED');
24 END IF;
25
26 IF UPPER(:OLD.status) = 'TRUE' THEN
27 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count+1).X := :
old.X;
28 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).Y := :ol
d.Y;
29 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).CREATED :
= :old.CREATED;
30 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).STATUS :
= 'FALSE';
31 END IF;
32 END IF; --CLOSE IF FOR UPDATING
33
34 END BEFORE_DML_T1;
35 /
Trigger created.
SQL> CREATE OR REPLACE TRIGGER AFTER_DML_T1
2 AFTER UPDATE ON T1
3 BEGIN
4 IF UPDATING THEN
5 TRI_PKG.fire_on_insert := FALSE;
6 forall i in 1..TRI_PKG.v_dblogon.count()
7 INSERT INTO T1 values TRI_PKG.v_dblogon(i);
8 TRI_PKG.fire_on_insert := TRUE;
9 END IF;
10 END AFTER_DML_T1;
11 /
Trigger created.
SQL> select * from t1;
no rows selected
SQL> select * from audit_tab;
no rows selected
SQL> INSERT INTO T1 VALUES(1,99,SYSDATE,'SCOTT','TRUE');
SQL> /
1 row created.
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
1 99 02-DEC-16 SCOTT TRUE
SQL> DELETE FROM T1;
DELETE FROM T1
*
ERROR at line 1:
ORA-20002: data can not be deleted
ORA-06512: at "DB_ADMIN.BEFORE_DML_T1", line 7
ORA-04088: error during execution of trigger 'DB_ADMIN.BEFORE_DML_T1'
SQL> SELECT * FROM AUDIT_TAB;
ACTION WHO DTE X Y
---------- -------------------- --------- ---------- ----------
DELETE SCOTT 02-DEC-16
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
1 99 02-DEC-16 DB_ADMIN TRUE
SQL>
1 99 02-DEC-16 DB_ADMIN TRUE
SQL> UPDATE T1 SET X=2;
1 row updated.
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
2 99 02-DEC-16 DB_ADMIN TRUE
1 99 02-DEC-16 DB_ADMIN FALSE
SQL> UPDATE T1 SET X=3;
UPDATE T1 SET X=3
*
ERROR at line 1:
ORA-20002: FALSE RECORDS CAN NOT BE UPDATED
ORA-06512: at "DB_ADMIN.BEFORE_DML_T1", line 19
ORA-04088: error during execution of trigger 'DB_ADMIN.BEFORE_DML_T1'
SQL> UPDATE T1 SET X=4 WHERE X=2;
1 row updated.
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
4 99 02-DEC-16 DB_ADMIN TRUE
1 99 02-DEC-16 DB_ADMIN FALSE
1 99 02-DEC-16 DB_ADMIN FALSE
2 99 02-DEC-16 DB_ADMIN FALSE
2 99 02-DEC-16 DB_ADMIN FALSE
NOTE: DELETE and INSERT works as expected.
NOTE: UPDATE works fine If i try to update when it has single record and false status record.
the issue, if i try to update when it has multiple records. New records are getting inserted for all records in the table which I dont want.
when I wanted to update TRUE status record then new record must be inserted with previous values(status FALSE) and update an existing record value. And it should not insert new records for entire table. Please suggest.
Thank you for your support..!!!
Follow up of DMLTrigger
Priya, December 05, 2016 - 3:54 pm UTC
SQL> CREATE OR REPLACE package DB_ADMIN.TRI_pkg is
2 type t_dblogin is table of t1%rowtype index by pls_integer;
3 type t_audit is table of audit_tab%rowtype index by pls_integer;
4
5 v_dblogon tri_pkg.T_DBLOGIN;
6 v_audit tri_pkg.t_audit;
7 fire_on_insert boolean := true;
8
9 procedure delete_transaction(p_audit t_audit);
10 end;
11 /
Package created.
SQL>
SQL> CREATE OR REPLACE package body DB_ADMIN.TRI_pkg is
2 procedure delete_transaction(p_audit t_audit) is
3 pragma autonomous_transaction;
4 begin
5
6 INSERT INTO AUDIT_TAB(ACTION,WHO,DTE) VALUES('DELETE','SCOTT',SYSDATE);
7 COMMIT;
8
9 end;
10 end;
11 /
Package body created.
SQL> CREATE OR REPLACE TRIGGER DB_ADMIN.BEFORE_DML_T1
2 BEFORE DELETE OR INSERT OR UPDATE on T1
3 FOR EACH ROW
4
5 DECLARE
6 V_LOGIN_USER NUMBER;
7
8 BEGIN
9 IF DELETING THEN
10 tri_pkg.delete_transaction( tri_pkg.v_audit);
11 raise_application_error (-20002, 'data can not be delet
ed', true);
12 END IF; --CLOSE IF FOR DELETING
13
14 IF INSERTING THEN
15 if audit_pkg.fire_on_insert then
16 :NEW.CREATED := SYSDATE;
17 :NEW.CREATED_BY := USER ;
18 END IF;
19 END IF; -- CLOSE IF FOR INSERTING
20
21 IF UPDATING THEN
22 IF UPPER(:OLD.status) = 'FALSE' THEN
23 RAISE_APPLICATION_ERROR (-20002, 'FALSE RECORDS C
AN NOT BE UPDATED');
24 END IF;
25
26 IF UPPER(:OLD.status) = 'TRUE' THEN
27 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count+1).X := :
old.X;
28 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).Y := :ol
d.Y;
29 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).CREATED :
= :old.CREATED;
30 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).STATUS :
= 'FALSE';
31 END IF;
32 END IF; --CLOSE IF FOR UPDATING
33
34 END BEFORE_DML_T1;
35 /
Trigger created.
SQL> CREATE OR REPLACE TRIGGER AFTER_DML_T1
2 AFTER UPDATE ON T1
3 BEGIN
4 IF UPDATING THEN
5 TRI_PKG.fire_on_insert := FALSE;
6 forall i in 1..TRI_PKG.v_dblogon.count()
7 INSERT INTO T1 values TRI_PKG.v_dblogon(i);
8 TRI_PKG.fire_on_insert := TRUE;
9 END IF;
10 END AFTER_DML_T1;
11 /
Trigger created.
SQL> select * from t1;
no rows selected
SQL> select * from audit_tab;
no rows selected
SQL> INSERT INTO T1 VALUES(1,99,SYSDATE,'SCOTT','TRUE');
SQL> /
1 row created.
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
1 99 02-DEC-16 SCOTT TRUE
SQL> DELETE FROM T1;
DELETE FROM T1
*
ERROR at line 1:
ORA-20002: data can not be deleted
ORA-06512: at "DB_ADMIN.BEFORE_DML_T1", line 7
ORA-04088: error during execution of trigger 'DB_ADMIN.BEFORE_DML_T1'
SQL> SELECT * FROM AUDIT_TAB;
ACTION WHO DTE X Y
---------- -------------------- --------- ---------- ----------
DELETE SCOTT 02-DEC-16
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
1 99 02-DEC-16 DB_ADMIN TRUE
SQL>
1 99 02-DEC-16 DB_ADMIN TRUE
SQL> UPDATE T1 SET X=2;
1 row updated.
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
2 99 02-DEC-16 DB_ADMIN TRUE
1 99 02-DEC-16 DB_ADMIN FALSE
SQL> UPDATE T1 SET X=3;
UPDATE T1 SET X=3
*
ERROR at line 1:
ORA-20002: FALSE RECORDS CAN NOT BE UPDATED
ORA-06512: at "DB_ADMIN.BEFORE_DML_T1", line 19
ORA-04088: error during execution of trigger 'DB_ADMIN.BEFORE_DML_T1'
SQL> UPDATE T1 SET X=4 WHERE X=2;
1 row updated.
SQL> SELECT * FROM T1;
X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
4 99 02-DEC-16 DB_ADMIN TRUE
1 99 02-DEC-16 DB_ADMIN FALSE
1 99 02-DEC-16 DB_ADMIN FALSE
2 99 02-DEC-16 DB_ADMIN FALSE
2 99 02-DEC-16 DB_ADMIN FALSE
NOTE: DELETE and INSERT works as expected.
NOTE: UPDATE works fine If i try to update when it has single record and false status record.
the issue, if i try to update when it has multiple records. New records are getting inserted for all records in the table which I dont want.
when I wanted to update TRUE status record then new record must be inserted with previous values(status FALSE) and update an existing record value. And it should not insert new records for entire table. Please suggest.
Thank you for your support..!!!