Rollback and drop table while exception handling in pl sql - oracle

Here is the scenario, I am writing a procedure which performs DML(insert) operations on multiple tables. In case of exception I need to rollback data from most of two table and drop a newly created table.
Example:
Procedure{
1 create table1
2 insert into table2
3 insert into table3
Exception
WHEN OTHERS THEN
IF DBMS_SQL.IS_OPEN(cur) THEN
DBMS_SQL.CLOSE_CURSOR(cur);
END IF;
RAISE;
}
Now the question is that if insert fails on table2 then I need to drop table1 as well and if insert fails on table3 then I need to rollback the insert that has already happened on table2 and also drop table1.
I'm not sure how to do that in the "Exception" part of pl-sql procedure. Any help would be highly appreciated. Though I looked at this Create oracle table with auto commit on but this scenario doesn't match mine 100% (somewhat though). All suggestion will be highly apprecaited.

according to the question and comments, using SAVEPOINT is a good idea for this scenario:
1 create table1;
SAVEPOINT SPT;
2 insert into table2;
3 insert into table3;
Exception
WHEN OTHERS THEN
--your code;
ROLLBACK TO SPT;
DROP TABLE table1;

Related

How to get the table name in trigger on which ddl operation will be performed

I want to do something like
create or replace trigger t1 before ddl on database
begin
insert into table1 values(//the table name on which the ddl will be performed);
end;
so if i create a table named "Hello" than "Hello"(table name) will be inserted in table1
so i don't know how to fetch the table name actually
That's show here in the Database Docs
So you could do something like...
CREATE OR REPLACE TRIGGER ddl_trig
AFTER DDL
ON DATABASE
BEGIN
INSERT INTO loguser.ddl_log
(user_name, ddl_date, ddl_type,
object_type, owner,
object_name)
VALUES
(ora_login_user, SYSDATE, ora_sysevent,
ora_dict_obj_type, ora_dict_obj_owner,
ora_dict_obj_name);
END ddl_trig;
/
BUT, you shouldn't build your own auditing software/code. You should instead use the database's built in auditing system.
See this.
Disclaimer: I work for Oracle and am a product manager on the database team.

If a trigger on an Oracle DB table fails to execute successfully or does not compile, can it impact the base table?

let's say there are tables a and b.
There is an AFTER INSERT trigger on a, that copies the row data to b.
If, during the execution of the trigger, there is an error does it impact table a in any way?
If the trigger does not compile, because it's ill-defined, does it impact table a?
I want to add a trigger to a table that is 'not mine'. I want to evaluate the risk that this can potentially pose.
Cheers
============== edit ================
I verified that - by handling the error (as suggested in the reply) - it now does not impact the base table.
create table tableA (column1 number);
create table tableB (column1 number, CONSTRAINT constraintName PRIMARY KEY (column1));
create or replace TRIGGER tableA_trigger
AFTER INSERT ON tableA
FOR EACH ROW
DECLARE
--
BEGIN
insert into tableB values (1);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error occured but ignored.');
END;
insert into tableA values (1);
insert into tableA values (1);
After that, tableA had two records, tableB only 1.
Without the exception handling, both tables would only have one record each, and after the first insert it would have shown an exception in the SQL Developer window.
Yes, it will impact the base table.
If there is error in insert trigger it will not allow to insert any record in base table. Same applies to all type of triggers.
Also, Adding DML triggers to tables affects the performance of DML statements on those tables.
According to oracle documentation:
If a predefined or user-defined error condition or exception is raised
during the execution of a trigger body, then all effects of the
trigger body, as well as the triggering statement, are rolled back
(unless the error is trapped by an exception handler). Therefore, a
trigger body can prevent the execution of the triggering statement by
raising an exception. User-defined exceptions are commonly used in
triggers that enforce complex security authorizations or integrity
constraints.

Mutating table exception is not occurring

I have created a trigger and was expecting a mutating table error in below case but didn't get one through normal insert but getting an error while inserting using a query. I am not sure which concept I am missing here.
drop table temp;
create table temp (id number,name varchar2(500),is_active number);
create or replace trigger temp_trg before insert on temp
for each row
declare
v_count number;
begin
select count(1) into v_count from temp;
update temp set is_active=0 where is_active=1 and id=:new.id;
end;
/
select * from temp;
insert into temp values (1,'xyz',1);
insert into temp values (1,'xyz',1);
insert into temp select 1,'xyz',1 from dual;
getting an error while inserting using a query.
Mutating table occurs when we query the table which owns the trigger. Specifically it happens when Oracle can't guarantee the outcome of the query. Now when you insert a single row into the table Oracle can predict the outcome of the query when the FOR EACH ROW trigger fires, because it's a single row.
But with an INSERT FROM query Oracle is confused: should the count be the final figure including all the rows selected by the query or just a rolling count? The answer seems straightforward in this case but it's easy to imagine other queries where the answer is not clear cut. Rather than evaluate each query on its predictability Oracle enforces a plain fiat and hurls ORA-04091 for all query driven inserts.
The restriction on Mutating Tables applies to all triggers that use FOR EACH ROW clause except when either of the following is true:
the trigger fires on BEFORE event, i.e. the data wasn't actually changed
it is known that only one row will be affected - INSERT ... VALUES is the only DML that meets this condition

Using commit in Trigger in Oracle 11g

I have created the following trigger in oracle-
create or replace TRIGGER TODAY_TD_INSERT AFTER INSERT ON table1
FOR EACH ROW
DECLARE
BEGIN
INSERT INTO table2 (col1
,col2
,col3
)
VALUES (:NEW.,col1
,:NEW.,col2
,:NEW.,col3
);
END;
So if any data insert in table1 the same data is inserting in table 2 also. So my question is do i need commit the data in new table2 ? So far i know we cannot use commit from trigger.
No, you do not. The session that made the change to the table on which the trigger is placed issues the commit (or rollback), and that applies to all changes made by that session.

Reading current table id during trigger operation

I have three tables: A and B
The relationship is A can have many B
So B has a reference to the A.id as one of its columns
Table A
|id|date|...
Table B
|id|A_id|...
I have created an Oracle trigger on table A so that when it is updated it updates an A_Mod table.
This trigger is
CREATE OR REPLACE TRIGGER TR_A_INSERT_UPDATE
AFTER INSERT OR UPDATE ON A
FOR EACH ROW
BEGIN
INSERT INTO A_Mod values(..., :new.date, ...)
END;
This works fine :)
My problem si creating the trigger for table B.
The trigger is:
CREATE OR REPLACE TRIGGER TR_B_INSERT_UPDATE
AFTER INSERT OR UPDATE ON B
FOR EACH ROW
DECLARE
ts TIMESTAMP;
BEGIN
SELECT aa.date INTO ts FROM B bb
INNER JOIN A aa ON a.id = bb.A_id
WHERE bb.id = :new.id;
INSERT INTO A_Mod values(..., :new.date, ...)
END;
This trigger is reading the ID of the updated line in table B and then getting the date from the corresponding row in table A. It then tries to insert it into A_Mod
The problem is I get a mutating error
Error report:
SQL Error: ORA-04091: table B is mutating, trigger/function may not see it
ORA-06512: at "TR_B_INSERT_UPDATE", line 5
ORA-04088: error during execution of trigger 'TR_B_INSERT_UPDATE'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
Looking at the docs I can remove this error by removing the FOR EACH ROW line and having the trigger fire once per statement rather than once per row. Unfortunately I am using an ORM mapper so do not control how the updates happen. I think there would be times when the update could cover multiple rows.
The docs say something about creating a temporary table but I am not sure how this would help. Would I have to create a temporary table inside the trigger, create a trigger on this temporary table which then updates A_Mod, update this temporary table when the trigger is fired and then delete everything after?
Any tips greatly appreciated.
Thanks
It doesn't appear that there is any reason in your trigger on B to query the B table. You should be able to simply query A using the :new.a_id
CREATE OR REPLACE TRIGGER TR_B_INSERT_UPDATE
AFTER INSERT OR UPDATE ON B
FOR EACH ROW
DECLARE
ts TIMESTAMP;
BEGIN
SELECT a.date
INTO ts
FROM A a
WHERE a.id = :new.a_id;
INSERT INTO A_Mod values(..., :new.date, ...)
END;
From a data modeling standpoint, however, I would be very concerned if a trigger on a child table needed to query any information from the parent table or to insert data into the same history table that the parent table writes to. That seems likely to indicate a normalization issue.

Resources