I am told by someone that when calling Oracle from ADO.net, when calling multiple inserts in a loop, where each insert causes a trigger to fire that includes within it's PL-Sql a Commit statement, that it is impossible to stop that commit from actually commiting the transaction.
i.e., I want my ADO.Net code to begin a transaction before the loop starts, and, when the loop exits, only commit all the inserts if and only if every insert in the loop was successful. My source is telling me that the way Oracle works, if these triggers include COmmit statements, then this is impossible..
As this seems to be an very common requirement, and I know it is possible in SQL Server, this does not seem right to me.
Is this correct?
Your informant is wrong, if he is talking about Oracle database triggers:
1) You cannot put a COMMIT in an Oracle trigger that is not autonomous:
SQL> create trigger this_wont_work
2 after insert on emp
3 begin
4 commit;
5 end;
6 /
Trigger created.
SQL> insert into emp (empno) values (123)
2 /
insert into emp (empno) values (123)
*
ERROR at line 1:
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at "TONY.THIS_WONT_WORK", line 2
ORA-04088: error during execution of trigger 'TONY.THIS_WONT_WORK'
2) If the trigger is autonomous (i.e. has PRAGMA AUTONOMOUS_TRANSACTION in its declaration section) then it can only commit any changes it (the trigger) makes.
There is no danger whatsoever of a trigger committing work you did outside of that trigger.
Note: the use of autonomous transactions in triggers is dangerous except for certain cases, because actions performed by the autonomous trigger will be committed even if the triggering statement is rolled back. This can easily lead to data corruption if mis-used.
Related
I need a clarity about pragma autonomous transaction. I have used a overlap select query in trigger and insert query in procedure. If I import two records in single file, the 2nd record is same as 1st record so the second record should be shown as overlap error. Now the select query in trigger executes but the error is not thrown by using the pragma autonomous transaction.
AUTONMOUS_TRANSACTION is a nested transaction. It executes DML independently of the calling transaction. So a query issued in an autonomous transaction won't see any uncommitted changes in the outer transaction. This is why you don't see your error message: the invalid state only exists in the uncommitted changes of the transaction.
Obviously you are using AUTONMOUS_TRANSACTION to avoid a mutating table error. However, a better solution would be to use a COMPOUND DML trigger: use a FOR EACH ROW to store the changes in an array, then verify them for no overlaps in the AFTER statement stage. Find out more.
I have 3 tables in oracle DB. I am writing one procedure to delete some rows in all the 3 tables based on some conditions.
I have used all three delete statements one by one in the procedure. While executing the mentioned stored procedure, is there any auto-commit happening in the at the time of execution?
Otherwise, Should I need to manually code the commit at the end?
There is no auto-commit on the database level, but the API that you use could potentially have auto-commit functionality. From Tom Kyte.
That said, I would like to add:
Unless you are doing an autonomous transaction, you should stay away from committing directly in the procedure: From Tom Kyte.
Excerpt:
I wish PLSQL didn't support commit/rollback. I firmly believe
transaction control MUST be done at the topmost, invoker level. That
is the only way you can take these N stored procedures and tie them
together in a transaction.
In addition, it should also be noted that for DDL (doesn't sound like you are doing any DDL in your procedure, based on your question, but just listing this as a potential gotcha), Oracle adds an implicit commit before and after the DDL.
There's no autocommit, but it's possible to set commit command into stored procedure.
Example #1: no commit
create procedure my_proc as
begin
insert into t1(col1) values(1);
end;
when you execute the procedure you need call commit
begin
my_proc;
commit;
end;
Example #2: commit
create procedure my_proc as
begin
insert into t1(col1) values(1);
commit;
end;
When you execute the procedure you don't nee call commit because procedure does this
begin
my_proc;
end;
There is no autocommit with in the scope of stored procedure. However if you are using SQL Plus or SQL Developer, depending on the settings autocommit is possible.
You should handle commit and rollback as part of the stored procedure code.
I have 2 triggers on a table A one is BEFORE INSERT and one AFTER INSERT :
CREATE OR REPLACE TRIGGER DEMO_TRG
AFTER INSERT
ON A
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
declare
PRAGMA AUTONOMOUS_TRANSACTION;
begin
UPDATE A
SET STATUS = 'DONE'
WHERE id_id =:new.p_id;
commit;
END;
/
Why the update command in the after insert doesn't work (the status is not set to DONE)? Did I missed something?
Most likely the issue is due to the AUTONOMOUS_TRANSACTION - when you do that, you're effectively switching to a different transaction, which won't know about your uncommitted transaction (ie. your insert), so there is nothing for it to update.
What you should do is amend the column (:new.status := 'DONE') in your before trigger. Or, even better, avoid triggers and have the logic in a stored procedure and don't allow anyone to directly insert into the table, although I appreciate this may be a big switch if you have lots of applications inserting directly into tables etc
Let's say I have 2 tables, EMPLOYEE and EMP_BAK. I need to create a backup table for all the data that deleted from employee, even these that are rolled back
My trigger:
CREATE OR REPLACE TRIGGER emp_del_bak_trg
before delete ON employee
FOR EACH row
DECLARE
oldname department.department_name%type;
newname department.department_name%type;
BEGIN
INSERT INTO emp_bak
VALUES (:OLD.employee_id, :OLD.employee_name, :OLD.job
,:OLD.hire_date,:OLD.department_id, sysdate);
--commit;
end;
Now, if I rollback then the data will be deleted; if I uncomment out commit, I'll get an error on deleting. The idea is to keep the record plus keep track of the system updates.
Any ideas how to get around this?
There is almost never a good reason to commit inside a trigger.
Now, that's out of the way your situation seems to be extra special, you need to track it, even if the transaction is rolled back. This is a fairly unusual requirement, but I'm going to assume you have a very good reason for doing this.
If you really, really, want to do this you need to use an autonomous transaction, this enables an independent transaction to be committed within another. As a trigger is a PL/SQL block, you can do this in a trigger.
The Oracle documentation has a separate section dealing with autonomous transactions in triggers, with plenty of examples for you. Wherever you need to use one the syntax is as follows, this is always placed in the DECLARE block:
PRAGMA AUTONOMOUS_TRANSACTION;
Your trigger, therefore, would look as follows:
create or replace trigger emp_del_bak_trg
before delete on employee
for each row
declare
PRAGMA AUTONOMOUS_TRANSACTION;
begin
insert into emp_bak
values ( :old.employee_id, :old.employee_name, :old.job
, :old.hire_date, :old.department_id, sysdate);
commit;
end;
/
As a little note I always case this differently so it's obvious you're doing something that you shouldn't be. Autonomous transactions are dangerous and should be used with extreme care.
I have following procedure:
procedure mayFailProc() as
begin
insert into t1 (id, val) values (1, '123');
insert into t1 (id, val) values (2, '123');
insert into t1 (id, val) values (3, '123'); //fails, i.e. due to pk uniqueness error
end;
this exception thrown in mayFailProc is a normal thing and it is handled by its caller. So the transaction is not rolled back and execution continues as if there was no exception in mayFailProc. I wonder what will happen to first two successfully executed inserts? Will they be retained or not?
See here for Oracle's explanation. You can jump to the How Oracle Does Implicit Rollbacks section to start.
Before executing an INSERT, UPDATE, or DELETE statement, Oracle marks
an implicit savepoint (unavailable to you). If the statement fails,
Oracle rolls back to the savepoint. Usually, just the failed SQL
statement is rolled back, not the whole transaction. If the statement
raises an unhandled exception, the host environment determines what is
rolled back.
More:
You should explicitly commit or roll back every transaction. Whether
you issue the commit or rollback in your PL/SQL program or from a
client program depends on the application logic. If you do not commit
or roll back a transaction explicitly, the client environment
determines its final state.
For example, in the SQL*Plus environment, if your PL/SQL block does
not include a COMMIT or ROLLBACK statement, the final state of your
transaction depends on what you do after running the block. If you
execute a data definition, data control, or COMMIT statement or if you
issue the EXIT, DISCONNECT, or QUIT command, Oracle commits the
transaction. If you execute a ROLLBACK statement or abort the SQL*Plus
session, Oracle rolls back the transaction.
Just done a quick test... the rows in my test were not retained when the third insert caused a "unique constraint ... violated" error