oracle trigger



前提要:系统时间触发器是指基于Oracle系统事件(LOGIN登录  STARTUP启动)所建立的触发器,通过使用系统事件触发器,提供了跟踪系统或数据库变化的机制。

 

1.常用事件属性函数

建立系统触发器要用到的:

ora_client_ip_address:用于返回客户端的IP地址 ora_database_name:用于返回当前数据库名 ora_des_encrypted_password:用于返回DES加密后的用户口令 ora_dict_obj_name:用于返回DDL操作所对应的数据库对象名 ora_dict_obj_name_list(name_list_ OUT ora_name_list_t):用于返回字事件中被修改的对象名列表 ora_dict_obj_owner:用于返回DDL操作所对应的对象的所有者名。 ora_dict_obj_ower_list(ower_list OUT ora_name_list_t):用于返回在事件中被修改对象的所有者列表 ora_dict_obj_type:用于返回DDL操作所对应的数据库对象的类型。 ora_grantee(user_list OUT ora_name_list_t):用于返回授权时事件授权者。 ora_instance_num:用于返回历程号。 ora_is_alter_column(column_name IN VARCHAR2):用于检测特定列是否被修改 ora_is_creating_nested_table:用于检测是否正在建立嵌套表 ora_is_drop_column(column_name IN VARCHAR2):用于检测特定列是否被删除 ora_is_servererror(error_number):用于检测是否返回了特定Oracle错误。 ora_login_user:用于返回登录用户名 ora_sysevent :用于返回触发 触发器的系统时间名。

 

 

2.建立例程启动和关闭触发器:

为了跟踪例程启动和关闭事件,可以分别建立例程启动触发器和历程关闭触发器

为了记载历程启动和或关闭事件和时间,首先建立事件表event_table:

 

 

conn sys/oracle as sysdba

create table event_table(event varchar2(30),time date);

 

在建立了事件表event_table之后,就可以在触发器中引用该表了。

例程启动触发器和关闭触发器只有特权用户才能建立,例程启动触发器只能使用AFTER关键字,而例程关闭触发器只能使用BEFORE关键字

CREATE OR REPPLACE TRIGGER tr_startup

AFTER STARTUP ON DATABASE

BEGIN

INSERT INTO event_table VALUES(ora_sysevent,SYSDATE);

END;

/

 

CREATE OR REPLACE TRIGGER tr_shutdown

BEFORE SHUTDOWN ON DATABASE

BEGIN

INSERT INTO event_table VALUES(ora_sysevent,SYSDATE);

END;

/

 

在建立了tr_startup触发器之后,当打开数据库之后会执行该触发器相应代码,在建立触发器tr_shutdown之后,在关闭例程之前,会执行触发器的相应代码,但SHUTDOWN ABORT(关闭数据库)不会触发该触发器。

 

 

 

3.建立登录和退出触发器

为了记载用户登录和退出事件,可以分别建立登录和退出触发器。为了记载登录用户和退出用户的名称。时间和IP地址,应该首先建立专门存档登录和退出的信息表LOG_TABLE

 

conn sys/oracle as sysdba

CREATE TABLE log_table(

username VARCHAR2(20),login_time DATE,

logoff_time DATE,address VARCHAR2(20)

);

 

在建立了LOG_TABLE表之后,就可以在触发器中引用该表了。

要用特权身份用户来建立登录和退出触发器,并且登录触发器只能使用AFTER关键字,而退出触发器用BEFORE

 

CREATE OR REPLACE TRIGGER tr_logon

AFTER LOGIN ON DATABASE

BEGIN

INSERT INTO log_table(username,logon_time,address)

VALUES(ora_login_user,SYSDATE,ora_client_ip_address);

END;

/

 

CREATE OR REPLACE TRIGGER tr_logoff

BEFORE LOGOFF ON DATABASE

BEGIN

INSERT INTO log_table(username,logoff_time,address)

VALUES(ora_login_user,SYSTEM,ora_client_ip_address);

END;

/

 

在建立了触发器tr_logon之后,当用户登录到数据库之后,会执行其触发器代码;在建立了触发器tr__logoff之后,当用户断开数据库连接之前,会执行其触发器代码。

 

 

4.建立DDL触发器

 

为了记载系统所发生的DDL事件(CREATE,ALTER,DROP),可以建立DDL触发器,为了记载DDL时间信息,应该建立专门的表,以便存放DDL事件信息。

conn sys/oracle as sysdba

CREATE TABLE event_ddl(

event VARCHAR2(20),username VARCHAR2(10),

owner VARCHAR2(10),obbjname VARCHAR2(20),

objtype VARCHAR2(10),time DATE

);

 

在建立了表event_ddl之后,就可以在触发器中引用该表,为了记载DDL事件,应该建立DDL触发器,注意,当建立DDL触发器时,必须使用AFTER关键字。

 

CREATE OR REPLACE TRIGGER tr_ddl

AFTER DDL ON scott.schema

BEGIN

INSERT INTO event_ddl VALUES(

ora_sysevent,ora_login_user,ora_dict_obj_owner,ora_dict_obj_name,ora_obj_type,SYSTEM

);

END;

/

 

当建立了触发器tr_dll之后,如果在SCOTT方案对象上执行了DDL操作,则会将该新息记载到表event_ddl中。


**************************************************************************************************************************************

  1. --事件属性函数  
  2. --客户端的ip地址  
  3. select ora_client_ip_address from dual  
  4. --当前数据库名  
  5. select ora_database_name from dual  
  6. --登录用户名  
  7. select ora_login_user from dual  
  8. --触发触发器的系统事件名ora_sysevent  
  9. --ddl操作所对应的对象的所有者名ora_dict_obj_owner  
  10. --ddl操作所对应的数据库对象名ora_dict_obj_name  
  11. --ddl操作所对应的数据库对象的类型ora_dict_obj_type  
  12. --事件触发器(未测试),事件触发器需特定用户才能创建  
  13. --数据库启动,关闭触发器  
  14. create or replace trigger tr_startupafter   
  15. startup on databasebegin   
  16. insert into event_table values(ora_sysevent,sysdate);  
  17. end;  
  18. /  
  19.   
  20. create or replace trigger tr_shutdownbefore   
  21. shutdown on databasebegin   
  22. insert into event_table values(ora_sysevent,sysdate);  
  23. end;  
  24. /  
  25.   
  26. --用户登录,退出触发器  
  27. create or replace trigger tr_logonafter   
  28. logon on databasebegin   
  29. insert into log_table values(ora_login_user,sysdate,ora_client_ip_address);  
  30. end;  
  31. /  
  32.   
  33. create or replace trigger tr_logoffbefore   
  34. logoff on databasebegin insert   
  35. into log_table values(ora_login_user,sysdate,ora_client_ip_address);  
  36. end;  
  37. /  
  38.   
  39. --建立ddl触发器  
  40. create or replace trigger tr_ddlafter ddl on scott.schemabegin   
  41. insert into ddl_table values(ora_sysevent,ora_login_user,ora_dict_obj_owner, ora_dict_obj_name,ora_dict_obj_type,sysdate);  
  42. end;  
  43. /  
  44. --事件属性函数
    --客户端的ip地址
    select ora_client_ip_address from dual
    --当前数据库名
    select ora_database_name from dual
    --登录用户名
    select ora_login_user from dual
    --触发触发器的系统事件名ora_sysevent
    --ddl操作所对应的对象的所有者名ora_dict_obj_owner
    --ddl操作所对应的数据库对象名ora_dict_obj_name
    --ddl操作所对应的数据库对象的类型ora_dict_obj_type
    --事件触发器(未测试),事件触发器需特定用户才能创建
    --数据库启动,关闭触发器
    create or replace trigger tr_startupafter 
    startup on databasebegin 
    insert into event_table values(ora_sysevent,sysdate);
    end;
    /
    
    create or replace trigger tr_shutdownbefore 
    shutdown on databasebegin 
    insert into event_table values(ora_sysevent,sysdate);
    end;
    /
    
    --用户登录,退出触发器
    create or replace trigger tr_logonafter 
    logon on databasebegin 
    insert into log_table values(ora_login_user,sysdate,ora_client_ip_address);
    end;
    /
    
    create or replace trigger tr_logoffbefore 
    logoff on databasebegin insert 
    into log_table values(ora_login_user,sysdate,ora_client_ip_address);
    end;
    /
    
    --建立ddl触发器
    create or replace trigger tr_ddlafter ddl on scott.schemabegin 
    insert into ddl_table values(ora_sysevent,ora_login_user,ora_dict_obj_owner, ora_dict_obj_name,ora_dict_obj_type,sysdate);
    end;
    /
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

   本节的例子介绍了使用系统的LOGON/LOGOFF触发器来审计用户登录的情况。对于需要掌握用户在线时间的系统中十分有用。首先要创建一张表,该表保存用户登录的信息。然后创建三个触发器,分别对LOGON/LOGOFF和相关的SERVERERROR进行审计。

创建审计记录表:

CREATE TABLE log_audit (

login_date DATE,

logoff_date date,

username VARCHAR2(20),

user_ip varchar2(20),

error_code varchar2(15)

);

创建记录登录错误的触发器:

CREATE OR REPLACE TRIGGER log_errors AFTER SERVERERROR ON DATABASE

BEGIN

 IF (IS_SERVERERROR (1017)) THEN

 insert into system.log_audit (login_date,error_code) values(sysdate,'ORA-1017');

 END IF;

END;

 

创建登录审计触发器:

CREATE OR REPLACE TRIGGER logon_audit AFTER LOGON ON DATABASE

BEGIN

 insert into system.log_audit(login_date,username,user_ip) values(sysdate,user,ora_client_ip_address);

END;

 

创建退出审计触发器:

CREATE OR REPLACE TRIGGER logoff_audit BEFORE LOGOFF ON DATABASE

BEGIN

 insert into system.log_audit (logoff_date,username,user_ip) values(sysdate,user,ora_client_ip_address);

END;

/

   本例子可以对数据库的登录和退出记录日志。当触发器发生故障的时候(比如log_audit表所在的表空间满的时候,会导致用户登录失败。此时,需要使用具有ADMINISTER DATABASE TRIGGER权限的帐号登录,然后将审计所使用的触发器disabale,或者修正触发器的错误。因此在使用系统事件触发器的时候要十分谨慎。

 

1.5.4 使用系统级触发器禁用DDL语句

对于一个上线运行的系统,使用DDL操作是十分谨慎的。为了防止不必要的误操作,可以通过DDL触发器对某些表禁用DDL操作。可以在需要禁止的操作类型上使用raise_application_error函数来禁止操作,比如:

CREATE OR REPLACE TRIGGER DDL_FB before ALTER OR DROP OR RENAME

on schema 

BEGIN

 Raise_application_error(-20030,’此系统已经运行,不允许对表进行DDL维护’);

end;

对于设置了这类触发器的用户,如果需要修改表结构,那么需要首先把这个触发器关掉。

1.5.5 使用系统级触发器审计重要帐号的DDL语句

如果要审计数据库中的DDL操作,那么可以通过DDL触发器来实现,本节介绍一个例子,把数据库中的所有DDL操作都记录下来。本例子可以在oracle 8i或更高的版本中使用。

第一步,创建表空间和相关的日志表:

create tablespace statlog datafile '/oradata/statlog.dbf' size 200m;

 

create table stat$log_ddl

(

 ddl_date date,

 user_name varchar2(30),

 ip_addr VARCHAR2(30),

obj_name VARCHAR2(50),

 ddl_type      VARCHAR2(30),

 object_type   VARCHAR2(18),

 owner         VARCHAR2(30),

 SQL_TEXT      VARCHAR2(1000)

) TABLESPACE STATLOG;

第二步,创建数据库级的DDL触发器,把所有的DDL操作都记录下来

 

CREATE OR REPLACE TRIGGER DDL_audit AFTER CREATE OR ALTER OR DROP OR TRUNCATE OR

GRANT OR REVOKE OR RENAME

on DATABASE 

declare

 ipaddr varchar2(20);

 STEXT VARCHAR2(1000);

BEGIN

 begin

 select sys_context('USERENV', 'IP_ADDRESS') into ipaddr FROM dual;

 exception when others then

 ipaddr:='-';

 end;

 begin

 select SQL_TEXT INTO STEXT FROM v$open_cursor      WHERE UPPER(sql_text) LIKE 'ALTER%';

 exception when others then

 STEXT:='-';

 end;

 insert into sys.stat$log_DDL values

 (sysdate,

 user,

 nvl (ipaddr,'-'),

 NVL(ora_dict_obj_name,'-'),

 NVL(ORA_SYSEVENT,'-'),

 NVL(ora_dict_obj_type,'-'),

 NVL(ora_dict_obj_owner,'-'),

 STEXT

 );

exception when others then

 null;

end;

/

###################################################
Connected to Oracle Database 10g Enterprise Edition Release 10.2.0.1.0
Connected as tbcs
 
SQL>
SQL>
SQL> drop trigger tbcs.TRG_CJW_TEST;
 
drop trigger tbcs.TRG_CJW_TEST
 
ORA-04080: trigger 'TRG_CJW_TEST' does not exist
SQL> drop table tbcs.cjw_test;
 
drop table tbcs.cjw_test
 
ORA-00942: table or view does not exist
SQL> create table cjw_test
 (
 itemid1    varchar2(10),
 itemid2    varchar2(10),
 itemid3    varchar2(10)
 );
 
Table created
SQL> insert into cjw_test values('1','1','1');
row inserted
SQL> commit;
 
Commit complete
/*
对于after 类型的 for each row 级别的triggers,不论哪种insert语句触发了trigger,
都不允许在 trigger 中访问本trigger所依赖的table的
*/
SQL> create or replace trigger trg_cjw_test
   after insert on tbcs.cjw_test
   for each row
 declare
   i   number:=0;
 begin
   select count(1) into i from tbcs.cjw_test;
 end trg_cjw_test;
 /
 
Trigger created
 
SQL> insert into cjw_test values('1','1','1');
 
insert into cjw_test values('1','1','1')
 
ORA-04091: table TBCS.CJW_TEST is mutating, trigger/function may not see it
ORA-06512: at "TBCS.TRG_CJW_TEST", line 4
ORA-04088: error during execution of trigger 'TBCS.TRG_CJW_TEST'
 
SQL>
SQL>
/*
对于before 类型的 for each row 级别的triggers,如果使用 insert into ... values 语句触发此trigger ,
则在trigger 中访问本table没有问题;但如果使用 insert into select .. from 语句触发此trigger ,
则在trigger 中访问本table就报ora-04091错误;
*/
SQL> create or replace trigger trg_cjw_test
   before insert on tbcs.cjw_test
   for each row
 declare
   i   number:=0;
 begin
   select count(1) into i from tbcs.cjw_test;
 end trg_cjw_test;
 /
 
Trigger created
 
SQL> insert into cjw_test values('1','1','1');
row inserted
 
SQL> commit;
 
Commit complete
 
SQL>
SQL> create or replace trigger trg_cjw_test
   before insert on tbcs.cjw_test
   for each row
 declare
   i   number:=0;
 begin
   insert into tbcs.cjw_test
   select '2','2','2' from dual;
 end trg_cjw_test;
 /
 
Trigger created
 
SQL> insert into cjw_test values('1','1','1');
 
insert into cjw_test values('1','1','1')
 
ORA-04091: table TBCS.CJW_TEST is mutating, trigger/function may not see it
ORA-06512: at "TBCS.TRG_CJW_TEST", line 4
ORA-04088: error during execution of trigger 'TBCS.TRG_CJW_TEST'
ORA-06512: at "TBCS.TRG_CJW_TEST", line 4
ORA-04088: error during execution of trigger 'TBCS.TRG_CJW_TEST'
 
SQL>
 SQL>
/*
上面实验中使用before,我们在仅插入一条数据的情况下,并没有报错,现在试一下插入多条数据的情况
*/
SQL> create or replace trigger trg_cjw_test
   before insert on tbcs.cjw_test
   for each row
 declare
   i   number:=0;
 begin
   select count(1) into i from tbcs.cjw_test;
 end trg_cjw_test;
 /
 
Trigger created
 
SQL>
SQL> create table cjw_test_bak as select * from cjw_test;
 
Table created
SQL> insert into cjw_test_bak values('2','1','1');
row inserted
SQL> insert into cjw_test_bak values('3','1','1');
row inserted
SQL> insert into cjw_test_bak values('4','1','1');
row inserted
 
SQL> commit;
 
Commit complete
 
SQL> insert into cjw_test select * from cjw_test_bak;
 
insert into cjw_test select * from cjw_test_bak
 
ORA-04091: table TBCS.CJW_TEST is mutating, trigger/function may not see it
ORA-06512: at "TBCS.TRG_CJW_TEST", line 4
ORA-04088: error during execution of trigger 'TBCS.TRG_CJW_TEST'
 
SQL>



你可能感兴趣的:(ORACLE)