如何在该触发器中找到已调用触发器的sql语句的audsid,sql_id

时间:2019-07-12 18:04:19

标签: sql oracle triggers database-trigger

某些记录正在从表中删除,我们想确定从表中删除记录的sql语句,以便我们可以检查找到引起问题的程序。

我编写了以下内容,但sql

create or replace 
trigger find_del_abc
before delete on abc 
for each row 
declare 
    temp_audsid integer; 
    temp_sql_id VARCHAR2(13); 
    temp_prev_sql_id   VARCHAR2(13);  

begin 

  If deleting then 

     select sql_id, prev_sql_id, audsid into temp_sql_id, temp_prev_sql_id, temp_audsid from v$session where audsid = SYS_CONTEXT('USERENV','sessionid');

     insert into delete_abc_session 
     select * from v$session where audsid = temp_audsid; 

     insert into my_sql
     select sql_id, sql_fulltext from v$sqlarea where sql_id in (temp_sql_id, temp_prev_sql_id); 

  End If; 


end;

但是我在my_sql中看不到“从abc删除” sql。 我在做错什么吗?

还有其他方法可以捕获调用触发器的sql语句的sql_id,prev_sql_id,audsid(在触发器块内部)。

在此方面,谢谢您的帮助。

1 个答案:

答案 0 :(得分:3)

  

还有其他方法可以捕获调用触发器的sql语句的sql_id,prev_sql_id,audsid(在触发器块内部)。

不容易。

好吧,AUDSID很简单:使用表达式SYS_CONTEXT('USERENV','SESSIONID')

据我所知,获取SQL_ID是不可能的,但 可能但很难获取SQL文本。

为此,您将需要在表上创建细粒度审核(FGA)策略。在FGA策略处理程序中,您可以访问SYS_CONTEXT('USERENV','CURRENT_SQL')。如果您的策略处理程序将其保存在某个地方,则触发器可以访问它。

不幸的是,您的触发器必须是AFTER触发器,因为BEFORE触发器将在FGA策略之前执行。

这是一个简单的例子:

创建测试表

--DROP TABLE matt1;
CREATE TABLE matt1 ( a number );

创建一个细粒度的审核策略处理程序以保存最后的SQL

CREATE OR REPLACE PACKAGE xxcust_record_last_sql_pkg AS
  -- TODO: you probably would want to store a collection of last SQL by table name
  l_last_sql VARCHAR2(32000);
  PROCEDURE record_last_sql (object_schema VARCHAR2, object_name VARCHAR2, policy_name VARCHAR2);
  FUNCTION get_last_sql RETURN VARCHAR2;
END xxcust_record_last_sql_pkg;
/

CREATE OR REPLACE PACKAGE BODY xxcust_record_last_sql_pkg AS

  PROCEDURE record_last_sql (object_schema VARCHAR2, object_name VARCHAR2, policy_name VARCHAR2) IS
  BEGIN
    xxcust_record_last_sql_pkg.l_last_sql := SUBSTR(SYS_CONTEXT ('userenv', 'CURRENT_SQL'),1,32000);
--    raise_application_error(-20001,'SQL = ' || xxcust_record_last_sql_pkg.l_last_sql);
  END record_last_sql;

  FUNCTION get_last_sql RETURN VARCHAR2 IS
  BEGIN
    RETURN xxcust_record_last_sql_pkg.l_last_sql;
  END get_last_sql;

END xxcust_record_last_sql_pkg;
/

注册FGA策略,以便在表的任何DML上调用处理程序

--EXEC DBMS_FGA.drop_policy (user, 'MATT1', 'MATT_TEST_POLICY');

BEGIN
   DBMS_FGA.add_policy (
          object_schema     => user,
          object_name       => 'MATT1',
          policy_name       => 'MATT_TEST_POLICY',
          audit_condition   => '1=1',
          audit_column      => null,
          handler_schema    => user,
          handler_module    => 'XXCUST_RECORD_LAST_SQL_PKG.RECORD_LAST_SQL',
          statement_Types   => 'INSERT,UPDATE,DELETE',
          enable            => TRUE);
END;
/

在我们的表上创建AFTER INSERT触发器以测试概念

--drop trigger matt1_ari1;

create or replace trigger matt1_ari1 after insert on matt1 for each row
begin
    raise_application_error(-20001, 'Invoking SQL was: ' || substr(xxcust_record_last_sql_pkg.get_last_sql,1,4000));
end;
/

全部测试

insert into matt1 (a) select 7*rownum from dual connect by rownum <= 5;
Error starting at line : 54 in command - insert into matt1 (a) select
7*rownum from dual connect by rownum <= 5 Error report - ORA-20001:
Invoking SQL was: insert into matt1 (a) select 7*rownum from dual
connect by rownum <= 5 ORA-06512: at "APPS.MATT1_ARI1", line 4
ORA-04088: error during execution of trigger 'APPS.MATT1_ARI1'

注意事项

我假设您有这样做的正当理由。很难做到这一点的一个原因是,没有通用的用例。有审计和安全性来控制对表的访问。实际上,我敢打赌,适当地单独使用细粒度的审核功能(即在表上没有自定义触发器)将是一种更好的方式来完成您想做的事情。