Skip to Main Content
  • Questions
  • Creation of a dynamic database trigger on a table

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Arun.

Asked: August 19, 2016 - 2:12 pm UTC

Last updated: August 23, 2016 - 1:11 am UTC

Version: 11g 11.2.0.3.0

Viewed 1000+ times

You Asked

Hi Tom,

I had asked a similar question earlier this month. For some reason, it is not letting me reply or update the same thread. Hence opening a new one.

I wanted to provide additional details on my question and request your help.

The requirement is to create a new oracle form to list a few tables & their columns. The user will chose the table and columns to audit. Based on their selection, a trigger has to be created dynamically. once created, the trigger will not change at least for an year. There will be only one user who would be authorized to chose the tables and columns to audit.

Have created a static trigger.
---------------------------------------------------------------------------------------
create or replace trigger TRIG_TEST_08052016
after insert or update or delete
on ARUN.trig_test
for each row

declare

log_id_v varchar2(10);
user_v varchar2(25);
change_type_v varchar2(2) ;
last_updated_by_v varchar2(25);
change_details_v varchar2(150);

-- Call this trigger from a procedure instead,
-- Not a good practise to use PRAGMA, Commit


-- PRAGMA AUTONOMOUS_TRANSACTION ;
/*

old_user_id ||'~'||new_user_id ||'~'||created_by ||'~'||old_test_num ||'~'||new_test_num ||'~'||
old_first_name ||'~'||new_first_name ||'~'||old_last_name ||'~'||new_last_name ||'~'||old_start_date ||'~'||
new_start_date ||'~'||old_end_date ||'~'||new_end_date ||'~'||old_salary ||'~'||new_salary ||'~'||
old_description ||'~'||new_description ||'~'||old_created_by ||'~'||new_created_by ||'~'||old_create_date||'~'||
current_date_time

*/


begin

dbms_output.put_line ('At 1');

select user into user_v from dual;

dbms_output.put_line('User: ' ||user_v) ;


if inserting then
dbms_output.put_line ('At 2');
change_type_v := 'I' ;

select user into user_v from dual;
last_updated_by_v := user_v;

change_details_v:= ' ' ||'~'||:new.user_id ||'~'||user_v ||'~'|| ' ' ||'~'|| :new.test_num ||'~'||
' ' ||'~'||:new.first_name ||'~'|| ' ' ||'~'|| :new.last_name ||'~'|| ' ' ||'~'||
:new.start_date ||'~'|| ' ' ||'~'||:new.end_date ||'~'|| ' ' ||'~'||:new.salary ||'~'||
' ' ||'~'||:new.description ||'~'|| ' ' ||'~'||:new.created_by ||'~'|| ' ' ||'~'||
to_char(sysdate,'DD-MON-YYYY HH24:MI:SS' ) ;

elsif updating then
dbms_output.put_line ('At 3');
change_type_v := 'U' ;
last_updated_by_v := :new.last_updated_by;

change_details_v:= :old.user_id ||'~'||:new.user_id ||'~'||user_v ||'~'||:old.test_num ||'~'||:new.test_num ||'~'||
:old.first_name ||'~'||:new.first_name ||'~'||:old.last_name ||'~'||:new.last_name ||'~'||:old.start_date ||'~'||
:new.start_date ||'~'||:old.end_date ||'~'||:new.end_date ||'~'||:old.salary ||'~'||:new.salary ||'~'||
:old.description||'~'||:new.description ||'~'||:old.created_by ||'~'||:new.created_by ||'~'||:old.create_date||'~'||
to_char(sysdate,'DD-MON-YYYY HH24:MI:SS' ) ;


elsif deleting then
change_type_v := 'D' ;
last_updated_by_v := :old.last_updated_by;

change_details_v:= :old.user_id ||'~'|| ' ' ||'~'||user_v ||'~'||:old.test_num ||'~'|| ' ' ||'~'||
:old.first_name ||'~'|| ' ' ||'~'||:old.last_name ||'~'|| ' ' ||'~'||:old.start_date ||'~'||
' ' ||'~'||:old.end_date ||'~'|| ' ' ||'~'||:old.salary ||'~'|| ' ' ||'~'||
:old.description||'~'|| ' ' ||'~'||:old.created_by ||'~'|| ' ' ||'~'||:old.create_date||'~'||
to_char(sysdate,'DD-MON-YYYY HH24:MI:SS' ) ;



end if;


-- :new.create_date := to_char(sysdate,'DD-MON-YYYY HH24:MI:SS' ) ;
-- :new.created_by := user_v ;

dbms_output.put_line ('At 4');


dbms_output.put_line ('At 5');

-- change_details_v := 'Arun'; --- ||'~'||'Tom' ; ---||'~'||:old.salary||'~'||:new.salary;

dbms_output.put_line ('At 6');
dbms_output.put_line ('change_type_v :' ||change_type_v);
dbms_output.put_line ('last_updated_by :' ||last_updated_by_v);
dbms_output.put_line ('change_details_v :' ||change_details_v);


dbms_output.put_line ('At 7');

select trig_log_logid.nextval
into log_id_v
from dual;

dbms_output.put_line ('At 8');

insert into trig_log_all_tables
(log_id,
dml_action,
updated_by,
details)
values
( log_id_v ,
change_type_v ,
last_updated_by_v ,
change_details_v
) ;

dbms_output.put_line ('At 9');

-- commit;

exception

when others then dbms_output.put_line ('Error: ' || SQLCODE ||'-'||Substr(SQLERRM,1,50));

end;
---------------------------------------------------------------------------------------------------

Test the trigger using :

BEGIN
insert_rows('trig_test', ' ''28'', ''56'',''Sam'',''Adams'',sysdate,sysdate,9001,''Testing'',''Sam'',''Sam'',sysdate');
commit;
END;
---------------------------------------------------------------------------------------------------

Could you please help.

I need your advise on how to pass the table & columns selected by the user to the dynamic trigger script. Also, if you could point me to any articles or books on this, I would appreciate it. I have tried researching online and have not found any articles that help.

Thank you



and Chris said...

Build a procedure that accepts the columns as an array. Then build up your create trigger statement and use execute immediate or dbms_sql to run it.

For example:

create or replace procedure p ( col_list dbms_sql.varchar2_table ) is
  create_trig varchar2(4000);
begin
  create_trig := 'create or replace trigger trg
after insert or update or delete
on ARUN.trig_test
for each row 
begin

  insert into trig_test_aud (';
  
  for i in col_list.first .. col_list.last loop
    create_trig := create_trig || col_list(i) || ',';
  end loop;
  
  --remove trailling comma
  create_trig := substr(create_trig, 1, length(create_trig)-1) || 
    ') values (';
  
  for i in col_list.first .. col_list.last loop
    create_trig := create_trig || ':new.' || col_list(i) || ',';
  end loop;
  
  create_trig := substr(create_trig, 1, length(create_trig)-1) || 
  ');
end;';

  execute immediate create_trig;

end p;
/


But as Connor said, I would always log all the columns. What happens if the person choosing the columns "forgets" to select some, so they can make unaudited changes?

You shouldn't really be letting someone pick and choose what to audit.

PS - I'm not sure why you can't add anything to your other question. You've already supplied a review. And the "Add Review" button displays for me:

https://asktom.oracle.com/pls/apex/f?p=100:11:::NO:RP:P11_QUESTION_ID:9531125800346509774

Rating

  (2 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Created a procedure with dbms_sql.varchar2_table

SAK, August 22, 2016 - 8:47 pm UTC

I was able to create a procedure exactly as per the code you provided.

I am trying to test the code for creating the dynamic trigger and getting an error when I try to call procedure DYNA_TRIG_CREATE and pass column_names : "wrong number or types of arguments in call to 'DYNA_TRIG_CREATE' "

Could you please let me know how to call this procedure and pass the column names to it.

Thanks and appreciate your help.

Regards



Chris Saxon
August 23, 2016 - 1:11 am UTC

Example

SQL> create or replace procedure p ( col_list dbms_sql.varchar2_table ) is
  2    create_trig varchar2(4000);
  3  begin
  4    for i in col_list.first .. col_list.last loop
  5      create_trig := create_trig || col_list(i) || ',';
  6    end loop;
  7
  8    dbms_output.put_line(create_trig);
  9  end p;
 10  /

Procedure created.

SQL>
SQL> set serverout on
SQL> declare
  2    x dbms_sql.varchar2_table;
  3  begin
  4    x(1) := 'My first line of code';
  5    x(2) := 'My second line of code';
  6    x(3) := 'My third line of code';
  7
  8    p(x);
  9  end;
 10  /
My first line of code,My second line of code,My third line of code,

PL/SQL procedure successfully completed.


Thank you !

SAK, August 23, 2016 - 4:01 pm UTC

Hi Tom and team,

Appreciate your help. Hope to see you at the Open World in Sep.

Regards

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library