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.
Related
How do I use the Commit statement correctly? Do I have to use commit statement for every insert statement and every function? For example, if I alter a table to add a column do I have to use a commit statement?
If you run a DDL statement (CREATE, ALTER, DROP, GRANT, etc) then:
Oracle Database implicitly commits the current transaction before and after every DDL statement.
If you run a DML statement (INSERT, UPDATE, DELETE, SELECT, CALL, MERGE) then:
These statements do not implicitly commit the current transaction.
and you will need to manually commit the uncommitted transactions (but you do not have to COMMIT after every statement).
If you want to perform a partial rollback then you can use SAVEPOINTs.
You should not put COMMIT statements in functions or procedures so that you can use multiple functions/procedures in a single transaction and then COMMIT or ROLLBACK that entire transaction.
Commits are needed when you change data in your tables (insert, update and delete). As long as you do not commit, other users will not see the changed data and you still have the option to undo the changes by executing a rollback.
There is no need to commit after every single statement.
Commits are not needed when changing the table itself (alter table). In fact, the alter table statement issues an implicit commit.
Some more on implicit/explicit commit statements in Oracle
Based on accumulated experience and "good practices" - you should not implement "commit" statements in your functions/procedures (most of the time, depends on the task, what are you try to achieve). The "commit" decision should lie on the user, who invokes your function/procedures.
What you can implement, is a "rollback", when exceptions are occurred/caught.
Often, when stored procedures or function are invoked from the application/web part, they are auto-committed (the option can be changed within transaction methods, as far i know, at least in java).
try like that:
try {
// First of all, let's begin a transaction
$db->beginTransaction();
// A set of queries; if one fails, an exception should be thrown
$db->query('first query');
$db->query('second query');
$db->query('third query');
// If we arrive here, it means that no exception was thrown
// i.e. no query has failed, and we can commit the transaction
$db->commit();
} catch (Exception $e) {
// An exception has been thrown
// We must rollback the transaction
$db->rollback();
}
In Oracle document http://docs.oracle.com/cd/E11882_01/server.112/e25789/transact.htm#CNCPT88952, it says:
A transaction begins when the first executable SQL statement is
encountered. An executable SQL statement is a SQL statement that
generates calls to a database instance, including DML and DDL
statements and the SET TRANSACTION statement.
When a transaction begins, Oracle Database assigns the transaction to
an available undo data segment to record the undo entries for the new
transaction.
My understandings of the preceding statements are,
SET TRANSACTION is one way to beginning a transaction;
a mile stone of the transaction beginning is available undo data segment is assigned;
transaction id is part of available undo data segment, therefore will be assigned after running the SET TRANSACTION.
But my following test doesn't satisfy my theory,
SQL> -- I suppose there's no other session is using this database
SQL> show rel;
release 1102000200
SQL> set transaction name 't1';
Transaction set.
SQL> SELECT XID, XIDUSN, XIDSLOT, STATUS from v$transaction;
no rows selected
It surprise me that there's no rows in transaction view. What's wrong with my hypothesis?
You skipped the last, most important sentence from the quoted text:
A transaction begins when the first executable SQL statement is encountered. An executable SQL statement is a SQL statement that generates calls to a database instance, including DML and DDL statements and the SET TRANSACTION statement.
When a transaction begins, Oracle Database assigns the transaction to an available undo data segment to record the undo entries for the new transaction. A transaction ID is not allocated until an undo segment and transaction table slot are allocated, which occurs during the first DML statement. A transaction ID is unique to a transaction and represents the undo segment number, slot, and sequence number.
SET TRANSACTION is not a DML (Data Manipulation Language), it is a transaction control statement, see this link for details: http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_1001.htm
You need to run DML statement in order to see the transaction is V$Transactions view.
======= EDIT - SET TRANSACTION syntax (parameters) ===============
See this link for details: http://docs.oracle.com/cd/E11882_01/server.112/e41084/statements_10005.htm#SQLRF01705
The following link on the PostgreSQL documentation manual http://www.postgresql.org/docs/8.3/interactive/populate.html says that to disable autocommit in postgreSQL you can simply place all insert statements within BEGIN; and COMMIT;
However I have difficulty in capturing any exceptions that may happen between the BEGIN; COMMIT; and if an error occurs (like trying to insert a duplicate PK) I have no way to explicitly call the ROLLBACK or COMMIT commands. Although all insert statements are automatically rolled back, PostgreSQL still expects an explicit call to either the COMMIT or ROLLBACK commands before it can consider the transaction to be terminated. Otherwise, the script has to wait for the transaction to time out and any statements executed thereafter will raise an error.
In a stored procedure you can use the EXCEPTION clause to do this but the same does not apply in my circumstance of performing bulk inserts. I have tried it and the exception block did not work for me because the next statement/s executed after the error takes place fails to execute with the error:
ERROR: current transaction is aborted, commands ignored until end of transaction block
The transaction remains open as it has not been explicitly finalised with a call to COMMIT or ROLLBACK;
Here is a sample of the code I used to test this:
BEGIN;
SET search_path TO testing;
INSERT INTO friends (id, name) VALUES (1, 'asd');
INSERT INTO friends (id, name) VALUES (2, 'abcd');
INSERT INTO friends (id, nsame) VALUES (2, 'abcd'); /*note the deliberate mistake in attribute name and also the deliberately repeated pk value number 2*/
EXCEPTION /* this part does not work for me */
WHEN OTHERS THEN
ROLLBACK;
COMMIT;
When using such technique do I really have to guarantee that all statements will succeed? Why is this so? Isn't there a way to trap errors and explicitly call a rollback?
Thank you
if you do it between begin and commit then everything is automatically rolled back in case of an exception.
Excerpt from the url you posted:
"An additional benefit of doing all insertions in one transaction is that if the insertion of one row were to fail then the insertion of all rows inserted up to that point would be rolled back, so you won't be stuck with partially loaded data."
When I initialize databases, i.e. create a series of tables/views/functions/triggers/etc. and/or loading in the initial data, I always use psql and it's Variables to control the flow. I always add:
\set ON_ERROR_STOP
to the top of my scripts, so whenever I hit any exception, psql will abort. It looks like this might help in your case too.
And in cases when I need to do some exception handling, I use anonymous code blocks like this:
DO $$DECLARE _rec record;
BEGIN
FOR _rec IN SELECT * FROM schema WHERE schema_name != 'master' LOOP
EXECUTE 'DROP SCHEMA '||_rec.schema_name||' CASCADE';
END LOOP;
EXCEPTION WHEN others THEN
NULL;
END;$$;
DROP SCHEMA master CASCADE;
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
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.