英文:
Sniff out the actual request that has activated a trigger
问题
I'm facing a problem in my work that has troubled the services for a couple weeks.
我在工作中遇到了一个问题,已经困扰了服务几周了。
I have written a trigger to try to catch the origin or a bug. but it's like searching a needle in a haystack.
我已经编写了一个触发器来尝试捕捉问题的根源或错误,但这就像在干草堆中寻找一根针一样困难。
Is there any way to include the DML transaction in a var so that i can see the actual transaction that is activating the trigger ?
有没有办法将DML事务包含在一个变量中,以便我可以看到触发器激活的实际事务?
Like imagine:
就像想象一样:
===> how do i register inside the V_catch this transaction:
===> 如何在V_catch中记录这个事务:
AFTER UPDATE
ON SYSADM.TABLE1 REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
WHEN (
NEW.COLUMN1 is null and new.COLUMN2 is not null and NEW.COLUMN in ('A','B')
)
DECLARE
[...]
V_catch clob;
begin
[...]
end;
i tried by doing some nasty jobs on it by writting custom if condition on every columns to try to get the changed values :
我尝试通过在每一列上编写自定义的if条件来做一些棘手的工作,以尝试获取更改的值:
v_catch := v_catch || :NEW.column1;
end if;
if :NEW.column2 is not null then
v_catch := v_catch || :NEW.column2;
end if;
That literally does not work as intended often times as we got notified of the bug, and in the report, v_catch was returning non-valuables values, and the bug could still not be located.
这实际上经常不像预期的那样工作,因为我们收到了有关错误的通知,而在报告中,v_catch返回的是无价值的值,错误仍然无法找到。
I'm pretty sure there could be a method out there to do that, but i'm still a beginners and self learning in PLSQL and SQL for oracle.
我相当确定可能有一种方法可以实现这一点,但我仍然是一个初学者,自学PLSQL和Oracle的SQL。
Maybe i overlooked something, maybe this is the only method existing.
也许我忽视了什么,也许这是唯一存在的方法。
can any veteran help me out on this pls?
请问有没有老手可以帮助我?
英文:
I'm facing a problem in my work that has troubled the services for a couple weeks.
I have written a trigger to try to catch the origin or a bug. but it's like searching a needle in a haystack.
Is there any way to include the DML transaction in a var so that i can see the actual transaction that is activating the trigger ?
Like imagine :
CREATE OR REPLACE TRIGGER SYSADM.WOW_TRIGGER
AFTER UPDATE
ON SYSADM.TABLE1 REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
WHEN (
NEW.COLUMN1 is null and new.COLUMN2 is not null and NEW.COLUMN in ('A','B')
)
DECLARE
[...]
V_catch clob;
begin
[...]
end;
===> how do i register inside the V_catch this transaction:
update table1 set column2 = "WHATEVER" where COLUMN15555 = 1
that happened following a customer's click in the software ?
i tried by doing some nasty jobs on it by writting custom if condition on every columns to try to get the changed values :
if :NEW.column1 is not null then
v_catch := v_catch || :NEW.column1;
end if;
if :NEW.column2 is not null then
v_catch := v_catch || :NEW.column2;
end if;
[...]
That literally does not work as intended often times as we got notified of the bug, and in the report, v_catch was returning non valuables values, and the bug could still not be located.
I'm pretty sure there could be a method out there to do that, but i'm still a beginners and self learning in PLSQL and SQL for oracle.
Maybe i overlooked something, maybe this is the only method existing.
can any veteran help me out on this pls?
答案1
得分: 1
审计确实是正确的选择。但考虑到您有使用审计的限制,触发器是您的下一个最佳选项。
问题在于,在没有DDL/系统事件触发器或细粒度审计的情况下(后者可以使用original_sql_txt
和SYS_CONTEXT('USERENV','CURRENT_SQL')
),我不认为在DML触发器中以清晰的方式获取触发SQL是可能的。虽然您可能会认为可以查询v$session.sql_id
,但这样做的查询将用其自身替换它。然而,以下是一个接近的方法,通过查看会话中哪些游标是打开的并且在过去的一秒钟内是活动的。
create table tmp55 (col1 integer);
create or replace trigger tr_tmp55 after insert or update or delete on tmp55 for each row
begin
FOR rec_cursor IN (SELECT oc.*,s.sql_fulltext
FROM v$open_cursor oc,
v$sql s
WHERE oc.sid = SYS_CONTEXT('USERENV','SID')
AND oc.child_address = s.child_address
AND oc.sql_id = s.sql_id
AND s.last_active_time >= SYSDATE - 1/86400
AND UPPER(sql_fulltext) LIKE '%TMP55%'
AND UPPER(sql_fulltext) NOT LIKE '%OPEN_CURSOR%')
LOOP
dbms_output.put_line(SUBSTR(rec_cursor.sql_fulltext,1,32676));
END LOOP;
end;
它可能会返回多行,其中只有一行是正确的SQL。为了缩小结果,假设DML会命名表格(而不是通过视图或同义词)。您需要将dbms_output
更改为插入到带有有用的标识会话信息的日志表中。显然,这只是一个初步的方案,可以进行调整,但它提供了基本思路。
英文:
Auditing is definitely the right answer. But given that you have a constraint against using auditing, triggers are your next best option.
The trouble is, outside of DDL/system-event triggers or fine-grain auditing (which can use original_sql_txt
and SYS_CONTEXT('USERENV','CURRENT_SQL')
, respectively), I don't think it's possible to get the triggering SQL in a DML trigger in a clean manner. While you'd think you could query v$session.sql_id
, the query to do so will replace it with itself. However, here's a hack that'll get close, by looking at what cursors are open by the session and were active in the past second.
create table tmp55 (col1 integer);
create or replace trigger tr_tmp55 after insert or update or delete on tmp55 for each row
begin
FOR rec_cursor IN (SELECT oc.*,s.sql_fulltext
FROM v$open_cursor oc,
v$sql s
WHERE oc.sid = SYS_CONTEXT('USERENV','SID')
AND oc.child_address = s.child_address
AND oc.sql_id = s.sql_id
AND s.last_active_time >= SYSDATE - 1/86400
AND UPPER(sql_fulltext) LIKE '%TMP55%'
AND UPPER(sql_fulltext) NOT LIKE '%OPEN_CURSOR%')
LOOP
dbms_output.put_line(SUBSTR(rec_cursor.sql_fulltext,1,32676));
END LOOP;
end;
It could return more than one row, only one of which is the correct SQL. Also to weed down results an assumption is made that the DML will name the table (rather than through a view or synonym). You'll want to change the dbms_output
to insert into a log table along with helpful identifying session info. Obviously this is rough and can be tweaked, but it gives the basic idea.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论