Need to insert machine name and DML command in table through trigger - oracle

I need to create a trigger that can insert in an audit table which DML command- insert update delete has been used on a base table plus with machine name.
Please guide me with this.

Let me show you an example:
1.A table my_test where I will make the dml operations
2.An audit table to record the operations
3.A Trigger to capture the values interested.
SQL> create table my_test ( c1 number, c2 number );
Table created.
SQL> create table audit_my_test ( id_timestamp timestamp,
session_id number,
username varchar2(30),
machine varchar2(50),
sql_text varchar2(400),
operation_type varchar2(20)
);
Table created.
SQL> create or replace trigger my_trg_my_test after insert or delete or update on my_test
referencing new as new old as old
for each row
declare
v_sql varchar2(400);
v_usr varchar2(40);
v_ope varchar2(20);
v_ter varchar2(50);
v_sid number;
begin
select sys_context('USERENV','SESSION_USER'),
sys_context('USERENV','CURRENT_SQL'),
sys_context('USERENV','SID'),
sys_context('USERENV','HOST')
into
v_usr,
v_sql,
v_sid,
v_ter
from dual;
IF INSERTING THEN
v_ope := 'Insert';
ELSIF UPDATING THEN
v_ope := 'Update';
ELSIF DELETING THEN
v_ope := 'Delete';
END IF;
insert into audit_my_test values ( systimestamp , v_sid, v_usr, v_ter, v_sql, v_ope );
end;
/
Trigger created.
SQL> show err
No errors
Now let's make some DML operations over the table
SQL> insert into my_test values ( 1 , 1) ;
1 row created.
SQL> insert into my_test values ( 2 , 2) ;
1 row created.
SQL> commit;
Commit complete.
Verify the audit table
ID_TIMESTAMP SESSION_ID USERNAME MACHINE
--------------------------------------------------------------------------- ---------- ------------------------------ ------------------------------
SQL_TEXT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OPERATION_
----------
24-JUL-20 01.01.25.567641 PM 328 SYS scglvdoracd0006.scger.dev.corp
Insert
24-JUL-20 01.01.45.514662 PM 328 SYS scglvdoracd0006.scger.dev.corp
Insert
ID_TIMESTAMP SESSION_ID USERNAME MACHINE
--------------------------------------------------------------------------- ---------- ------------------------------ ------------------------------
SQL_TEXT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OPERATION_
----------
You can read all the attributes from sys_context here:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions165.htm
The problem with this solution: You will never get the SQL responsible for the operation. CURRENT_SQL in SYS_CONTEXT only works inside a procedure and when it is invoked in a Fine Grain Access (FGA) policy as a handler.
If you need the SQL responsible for the operation, you either construct a FGA policy and a handler, or you use AUDIT which is much better in this specific case.

Related

In Oracle, how do i create stored procedure to insert value in 2 tables

I am trying to insert below details in two table. but it shows error. where i am wrong?
create or replace PROCEDURE ADD_customer_order(
customer_id in varchar, Shipping_id_arg in number,
order_date_arg in date, Total_price_arg in decimal,
inventory_id_arg in number, order_quantity_arg in number)
AS
BEGIN
INSERT INTO customer_order (customer_id,Shipping_id,Order_date,total_price) VALUES(customer_id_arg,Shipping_id_arg,order_date_arg, total_price_arg);
insert into order_details (inventory_id,order_quantity) values(scope_identity(),inventory_id_arg,order_quantity_arg);
END;
It helps if you format it nicely.
create or replace procedure add_customer_order(
customer_id in varchar2, shipping_id_arg in number,
order_date_arg in date , total_price_arg in decimal,
inventory_id_arg in number , order_quantity_arg in number)
as
begin
insert into customer_order
(customer_id , shipping_id , order_date , total_price)
values
(customer_id_arg, shipping_id_arg, order_date_arg, total_price_arg);
insert into order_details
( inventory_id , order_quantity)
values
(scope_identity(), inventory_id_arg, order_quantity_arg);
end;
Doing so, you easily note that the second INSERT is invalid, as you're inserting 3 values into 2 columns:
insert into order_details
( inventory_id , order_quantity)
values
(scope_identity(), inventory_id_arg, order_quantity_arg);
Either remove scope_identity() (what is it?), or include additional column into the column list you're inserting into.
After reading your comment, it seems that returning clause might help. See the following example (somewhat simpler than yours; didn't feel like typing that much). Trigger is used to auto-increment ORDER_ID column. in CUSTOMER_ORDER table (I'm on 11g XE; don't have identity columns here).
SQL> create table customer_order (order_id number, customer_id number);
Table created.
SQL> create table order_details (order_id number, inventory_id number);
Table created.
SQL> create sequence seqo;
Sequence created.
SQL> create or replace trigger trg_co
2 before insert on customer_order
3 for each row
4 begin
5 :new.order_id := seqo.nextval;
6 end;
7 /
Trigger created.
Procedure: note local variable declared in line #4 and returning clause in line #7:
SQL> create or replace procedure p_test
2 (par_customer_id in number, par_inventory_id in number)
3 is
4 l_order_id customer_order.order_id%type;
5 begin
6 insert into customer_order (customer_id) values (par_customer_id)
7 returning order_id into l_order_id;
8
9 insert into order_details (order_id, inventory_id)
10 values (l_order_id, par_inventory_id);
11 end;
12 /
Procedure created.
Testing:
SQL> exec p_test(100, 200);
PL/SQL procedure successfully completed.
SQL> exec p_test (235, 2230);
PL/SQL procedure successfully completed.
SQL> select * From customer_order;
ORDER_ID CUSTOMER_ID
---------- -----------
1 100
2 235
SQL> select * From order_details;
ORDER_ID INVENTORY_ID
---------- ------------
1 200
2 2230
SQL>
The same ORDER_ID value is used in both tables.

Oracle SQL - ORA-04079: invalid trigger insert

I am trying to create a trigger that runs when I insert in table 'cuenta' and make an insert in table 'cuenta_log', one of the values of this insert is gotten by accept input.
create or replace trigger trigger_new_account
AFTER INSERT ON cuenta FOR EACH ROW
accept vstring prompt "Please enter your name: ";
declare v_line varchar2(50);
begin
v_line:= 'Hello '||'&vstring';
insert into cuentas_log (fecha,cuenta,cliente)
values (now(),:new.idcuenta,v_line);
end;
cuenta_log structure is like that:
cuenta_log
("FECHA" DATE DEFAULT (sysdate),
"CUENTA" NUMBER(38,0),
"CLIENTE" VARCHAR2(50 BYTE)
)
Trigger fires on certain event; yours, after insert into table named cuenta. It doesn't work interactively with end users. You can't expect it to prompt anyone to enter anything. Besides, accept is a SQL*Plus command, it won't work in PL/SQL.
So, here's what you might have done: sample tables first:
SQL> create table cuenta (fecha date, cuenta number);
Table created.
SQL> create table cuenta_log (fecha date, cuenta number, cliente varchar2(30));
Table created.
Trigger:
SQL> create or replace trigger trigger_new_account
2 after insert on cuenta
3 for each row
4 begin
5 insert into cuenta_log(fecha, cuenta, cliente)
6 values
7 (sysdate, :new.cuenta, 'Hello ' || user);
8 end;
9 /
Trigger created.
Testing:
SQL> insert into cuenta (fecha, cuenta) values (sysdate, 100);
1 row created.
SQL> select * From cuenta;
FECHA CUENTA
------------------- ----------
11.05.2020 12:31:17 100
SQL> select * From cuenta_log;
FECHA CUENTA CLIENTE
------------------- ---------- ------------------------------
11.05.2020 12:31:17 100 Hello SCOTT
SQL>

What is the Oracle equivalent of TG_OP and TG_NAME in Postgres?

I want to create a trigger that just inserts the name of the modified table and the operation performed on it. Kind of like:
BEGIN
INSERT INTO audit_table VALUES(user,tg_name,tg_op);
END;
However I can't find what's the equivalent in Oracle to tg_name and tg_op from Postgres.
Huh, there's nothing that simple in Oracle, as far as I can tell (but I admit - I may be very wrong and someone might know better). Anyway, until that better answer comes, here's a little bit of coding; see if it helps.
I'm creating a test table and a log table (though, I'm not sure how good your log table actually is; you don't know when something was done etc. so - I took some freedom to add at least the DATE column).
SQL> create table test as select * From dept;
Table created.
SQL> create table test_log
2 (username varchar2(30),
3 c_date date,
4 table_name varchar2(30),
5 action varchar2(1));
Table created.
A trigger: unfortunately, I don't know whether there's a built-in function which returns name of an object (a table, in our case) you're doing something with, so I'm querying USER_OBJECTS.
SQL> create or replace trigger trg_test_log
2 after insert or update or delete on test
3 for each row
4 declare
5 l_table_name varchar2(30);
6 l_action varchar2(1);
7 begin
8 select object_name
9 into l_table_name
10 from user_objects
11 where object_id = dbms_rowid.rowid_object(nvl(:new.rowid, :old.rowid));
12
13 if inserting then
14 l_action := 'I';
15 elsif updating then
16 l_action := 'U';
17 elsif deleting then
18 l_action := 'D';
19 end if;
20
21 insert into test_log (username, c_date, table_name, action)
22 values (user, sysdate, l_table_name, l_action);
23 end;
24 /
Trigger created.
And ... action!
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> select * From test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> delete from test where deptno = 40;
1 row deleted.
SQL> update test set loc = 'CROATIA' where deptno = 10;
1 row updated.
SQL> insert into test (deptno, dname, loc) values (99, 'STACK', 'OVERFLOW');
1 row created.
SQL> select * From test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING CROATIA
20 RESEARCH DALLAS
30 SALES CHICAGO
99 STACK OVERFLOW
SQL> select * From test_log;
USERNAME C_DATE TABLE_NAME A
------------------------------ ------------------- ------------------------------ -
SCOTT 23.09.2018 08:44:17 TEST D
SCOTT 23.09.2018 08:44:17 TEST U
SCOTT 23.09.2018 08:44:17 TEST I
SQL>
There is no point in having special attribute for table_name and operation in DML trigger.
DML trigger can be created only on ONE table so you can hardcode its name in trigger itself.
Conditional predicates INSERTING, DELETING, and UPDATING can be used to derive operation name.
If, howevere, you want to create schema level trigger then you can use predefined attributes ora_*.
create table audit_table
(
user_name varchar2(30),
table_name varchar2(30),
action varchar2(30)
);
create or replace trigger schema_trigger
before create or alter
on schema
begin
insert into audit_table
(user_name, table_name, action)
values
(ora_login_user, ora_dict_obj_name, ora_sysevent);
end schema_trigger;
/
PL/SQL Triggers

Warning: Trigger created with compilation errors

I am trying to create a trigger in oracle like blow:
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT ON VC_PART_LIST FOR EACH ROW
BEGIN
SELECT VC_PART_LIST_SEQ.NEWTVAL INTO :new.SEQ_ID FROM dual;
END;
/
anyone can help me?Thx.
In sequence to fetch the next value use NEXTVAL . Below is the correct syntax
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT
ON VC_PART_LIST
FOR EACH ROW
BEGIN
SELECT VC_PART_LIST_SEQ.NEXTVAL INTO :new.SEQ_ID FROM DUAL;
END;
There are few issues in your code.
1) VC_PART_LIST_SEQ.NEWTVAL there is nothing called NEWTVAL in Oracle. It should be Nextval.
2) There is no need for a SQL statement.
See demo:
Trigger:
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT
ON VC_PART_LIST
FOR EACH ROW
BEGIN
:new.SEQ_ID := VC_PART_LIST_SEQ.NEXTVAL;
END;
/
Table:
create table VC_PART_LIST (SEQ_ID number,
VC_VTC varchar2(20), PART_NO varchar2(20), CONSUME_QTY varchar2(20));
Sequence
create sequence VC_PART_LIST_SEQ start with 1 increment by 1;
Insertion:
INSERT INTO VC_PART_LIST (VC_VTC, PART_NO, CONSUME_QTY)
VALUES ('6175SMFGD132000D', 'SC25R136', '11');
Commit;
Selection:
SQL> select * from VC_PART_LIST;
SEQ_ID VC_VTC PART_NO CONSUME_QTY
---------- -------------------- -------------------- --------------------
1 6175SMFGD132000D SC25R136 11

Triggers in Oracle and how to keep the records after a rollback

I need to create a trigger that writes changes in a shadow table. I know how to create the trigger but my challenge is that I need the records in the new table to exist even after a rollback.
This is an example of how the output will look like
INSERT INTO department VALUES (95, 'PURCHASING', 'CHICAGO');<br>
ROLLBACK;
1 rows inserted.
rollback complete.
SELECT * FROM department_log;
DEPARTMENT_ID DEPARTMENT_NAME ADDRESS OPERATION_TIME
---------------------- -------------------- -------------------- ------------------
90 HR CHICAGO 03-NOV-11
95 PURCHASING CHICAGO 03-NOV-11
SELECT * from department WHERE department_id >= 90;
DEPARTMENT_ID DEPARTMENT_NAME ADDRESS
---------------------- -------------------- --------------------
90 HR CHICAGO
You'll need to use autonomous transactions.
SQL> create table t (col1 number);
Table created.
SQL> create table t_shadow( col1 number, dt date );
Table created.
SQL> create trigger trg_t
2 before insert on t
3 for each row
4 declare
5 pragma autonomous_transaction;
6 begin
7 insert into t_shadow( col1, dt )
8 values( :new.col1, sysdate );
9 commit;
10 end;
11 /
Trigger created.
SQL> insert into t values( 1 );
1 row created.
SQL> rollback;
Rollback complete.
SQL> select * from t;
no rows selected
SQL> select * from t_shadow;
COL1 DT
---------- ---------
1 09-NOV-11
Note that if you find yourself using autonomous transactions for anything other than persistent logging, you are almost certainly doing something wrong. Autonomous transactions are a very dangerous and very frequently misused feature.
You would need to declare the trigger as an Autonomous Transaction
PRAGMA AUTONOMOUS_TRANSACTION;
This decouples the trigger code from the main transaction, so even if the main insertion into the table (which fired the trigger) rollsback, the trigger is executed in a different transactional context and can commit / rollback independently.

Resources