HelloDBA [English]
搜索Internet 搜索 HelloDBABA
  Oracle技术站。email: fuyuncat@gmail.com  MSN: fuyuncat@hotmail.com   acoug  acoug 

创建监控表的DML的触发器

[English]

作者: fuyuncat

来源: www.HelloDBA.com

日期: 2006-09-07 14:50:13

分享到  新浪微博 腾讯微博 人人网 i贴吧 开心网 豆瓣 淘宝 推特 Facebook GMail Blogger Orkut Google Bookmarks

在生产环境中,总是可能出现这样的情况:某张或者某些表的数据被莫名其妙的修改了,但是很难定位出是哪个用户、哪个过程修改的。这是一个很让DBA头痛的事情(往往DBA对于整个代码逻辑并不是非常了解)。要定位出“问题”语句,有几种方法可以选择:log miner;细节粒度审计;触发器。Log miner要求要有归档日志(这个并非所有系统都可以做),而且需要有相当的磁盘空间,好处就是可以离线做;细节粒度升级能够根据条件记录下表的DML操作(9i及之前只能记录SELECT语句),比较复杂的FGA需要较高权限的用户来实现;触发器比较灵活,能够按照比较复杂的条件来记录需要的信息。下面介绍触发器如何实现。

要建立这样的触发器,需要利用到几张系统视图:v$session, v$sql, v$cursor,(10g, 9.2.0.1中可以,9.2.0.5, 9.2.0.之前存在bug

 

SQL> connect "/ as sysdba"
      grant select on SYS.V_$SQL to demo;
      grant select on SYS.V_$SQL_BIND_DATA to demo;
      grant select on  SYS.V_$SQL_CURSOR to demo;
      grant select on  SYS.V_$SESSION to demo;
      grant create trigger to demo;

 

CREATE TABLE trig_sql(lt DATE, sid NUMBER, SERIAL# NUMBER,
                      USERNAME VARCHAR2(30), OSUSER VARCHAR2(64), 
                      MACHINE VARCHAR2(32), TERMINAL VARCHAR2(16), 
                      PROGRAM VARCHAR2(64), sqlText VARCHAR2(2000), 
                      status VARCHAR2(30));

 

方法1

create or replace trigger ttt_trig



  after insert or update on pga_ttt



DECLARE



   PRAGMA AUTONOMOUS_TRANSACTION;



begin



  INSERT INTO trig_sql 
     select sysdate,s.SID, s.SERIAL#, s.USERNAME, s.OSUSER, 
            s.MACHINE, s.TERMINAL, s.PROGRAM, q.sql_text line, 
            'NONE'



       from v$sql q, v$session s



      where s.audsid=(select userenv('SESSIONID') from dual) 



        and s.prev_sql_addr=q.address 



        AND s.PREV_HASH_VALUE = q.hash_value;



    



  COMMIT;



end;

 

方法2

create or replace trigger ttt_trig
  after insert or update on pga_ttt
DECLARE
   PRAGMA AUTONOMOUS_TRANSACTION;
begin
  for cr in (select s.SID, s.SERIAL#, s.USERNAME, s.OSUSER, 
                    s.MACHINE, s.TERMINAL, s.PROGRAM, 
                    q.sql_text line, c.status stat
       from v$sql q, v$sql_cursor c, v$session s
      where s.audsid=(select userenv('SESSIONID') from dual) 
        and s.prev_sql_addr=q.address 
        AND c.STATUS = 'CURFETCH')
  loop
INSERT INTO trig_sql VALUES(SYSDATE, cr.sid, cr.SERIAL#, 
                            cr.USERNAME, cr.OSUSER, cr.MACHINE, 
                                cr.TERMINAL, cr.PROGRAM, cr.line, 
                                cr.stat);
  end loop;
    
  COMMIT;
end;

 

第一种方法是通过前一SQL的地址(pre_sql_addr)和HASH(prev_hash_value)值来定位出发trigger的语句的,不能用sql_addresshash_value来定位,否则获取到是触发器里面向日志表插入记录数据的语句本身了。

第二个方法是通过通过地址加游标的方法,按照视图各个字段的解释,应该是可以通过v$sql_cursor.parent_handle来定位的。但是通过测试发现,只有当前一条语句和查找前一条语句的语句在一个PLSQL块中的时候才有效,

SQL> set serveroutput on
SQL> declare
  2      v_date date;
  3      v_sql varchar2(2000);
  4  begin
  5      select sysdate into v_date from dual;
  6
  7      select q.sql_text into v_sql
  8      from v$sql q, v$sql_cursor c, v$session s
  9      where s.audsid=(select userenv('SESSIONID') from dual)
 10    and s.prev_sql_addr=q.address and q.address=c.parent_handle;
 11
 12      dbms_output.put_line(v_sql);
 13  end;
 14  /
SELECT SYSDATE FROM DUAL
 
PL/SQL procedure successfully completed.

因为触发器本身是一个PLSQL块,所以总是无法获得正确语句,最后只有通过cursor的状态来获取。下面简单了解一下CURSOR各个状态的含义:

·         CURNULL:游标已经存在,但没有任何SQL语句在使用它(即cache在每个session内存中的游标)

·         CURSYNTAX:解析SQL语句过程的一个游标状态,说明调用游标的SQL语句语法正确,但是没有解析完成。

·         CURPARSE:调用游标的语句解析完毕

·         CURBOUND:游标使用了帮定变量,并定义好了帮定变量

·         CURFETCH:游标执行完毕,并fetch了数据

·         CURROW:游标正指向某一行

·         ERROR:游标错误,一般是有BUG了。

当一条INSERT或者UPDATE语句执行以后才会触发触发器,所以这时候的游标状态是CURFETCH,我们这就通过状态为CURFETCH来定位。

比较郁闷的是,我的系统是9.2.0.5的,因为存在BUG,这样的触发器做不了,FGA又无法监控INSERTUPDATE语句。还没找到好的办法。

  

Top

Copyright ©2005,HelloDBA.Com 保留一切权利

申明
by fuyuncat