Is there any other way to capture the sql_id, prev_sql_id, audsid of the sql statement which invoked the trigger (inside the trigger block).
Not easily.
Well, AUDSID is easy: use the expression SYS_CONTEXT('USERENV','SESSIONID')
Getting the SQL_ID, as far as I know, is impossible, but it is possible, though difficult, to get the SQL text.
To do so, you would need to create a Fine-Grained Auditing (FGA) policy on your table. Inside an FGA policy handler, you have access to SYS_CONTEXT('USERENV','CURRENT_SQL')
. If your policy handler saved that off somewhere, your trigger could have access to it.
Unfortunately, your trigger needs to be an AFTER trigger, because a BEFORE trigger will execute before the FGA policy.
Here is a quick example of the idea:
Create a test table
--DROP TABLE matt1;
CREATE TABLE matt1 ( a number );
Create a fine-grained auditing policy handler to save the last 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;
/
Register the FGA policy so that the handler is invoked on any DML of our table
--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;
/
Create an AFTER INSERT trigger on our table to test the concept
--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;
/
Test it all out
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'
Caveat
I am assuming that you have a legitimate reason to want to do this. One reason this is hard is that there aren't common use cases for this. There is auditing and security to control access to tables. In fact, I would bet that proper use of the fine-grained auditing feature by itself (i.e., no custom trigger on the table) would be a better way to do whatever you are trying to do.