Reputation: 1
As part of audit/history functionality I want to handle following scenario using AFTER UPDATE trigger or any other trigger, please let me know. Scenario --
I am using following trigger, giving deadlock scenario. Please advice to resolve this issue.
create table Base_table(
SYMBOL_ID NUMBER(9) primary key,
SYMBOL_NAME VARCHAR2(20) ,
PRICE NUMBER(9) ,
VERSION NUMBER(1)
)
organization index;
create table base_table_hist(
ID NUMBER(9) primary key,
SYMBOL_ID NUMBER(9) ,
SYMBOL_NAME VARCHAR2(20) ,
PRICE NUMBER(9),
VERSION NUMBER(1) ,
constraint other_symbolid foreign key(symbol_id) references test_symbol(symbol_id)
)
organization index;
************************************************************
create or replace Trigger Symbol_Ver
AFTER UPDATE ON Base_table
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
new_version number(5);
--Pragma AUTONOMOUS_TRANSACTION;
Sid number(9);
begin
if (:New.symbol_id <> :Old.symbol_id) OR (:New.price <> :Old.price) then
new_version:= :Old.version+1;
--insert into history table
insert into base_table_hist (id, symbol_id, symbol_name,price,version)
values (symbol_seq.nextval, :OLD.symbol_id, :OLD.symbol_name, :OLD.price, :OLD.version);
commit;
DBMS_OUTPUT.put_line('new_version..'||new_version);
end if;
if (:New.symbol_id <> :Old.symbol_id) OR (:New.price <> :Old.price) then
update base_table set version=new_version where symbol_id=:Old.symbol_id;
end if;
end;
Upvotes: 0
Views: 3357
Reputation: 67722
An autonomous transaction creates a new, independent transaction. So you're updating the same row with two transactions, leading to a deadlock.
You don't need an autonomous trigger here. In fact you don't want to touch the base table with DML in a trigger. That's always problematic.
Fortunately, here you can use a regular BEFORE
trigger (since you're updating a field):
CREATE OR REPLACE TRIGGER Symbol_Ver
BEFORE UPDATE ON Base_table
FOR EACH ROW
BEGIN
IF (:New.symbol_id <> :Old.symbol_id) OR (:New.price <> :Old.price) THEN
-- this will change the value in the row being updated
:new.version := :Old.version + 1;
--insert into history table
INSERT INTO base_table_hist
(id, symbol_id, symbol_name, price, version)
VALUES
(symbol_seq.nextval, :OLD.symbol_id,
:OLD.symbol_name, :OLD.price, :OLD.version);
-- COMMIT <-- don't commit in a trigger!
DBMS_OUTPUT.put_line('new_version..' || new_version);
END IF;
END;
An additional update on the base table would be both redundant and problematic since this would lead to a circular infinite recursion.
Also you can't commit in a trigger. You don't want to commit anyway, this breaks the transaction logic. Not committing allows the main transaction to roll back both the history table and the main table in one nice atomic chunk.
Upvotes: 1