views:

30

answers:

2

Hi,

I'm calling a third party stored procedure in Oracle from VB that is not rolling back.

Code first (I'm simplifying):

Connection string:

String sqlstr = "SERVER=x.x.x.x;Database=db;uid=sa;pwd=admin;Connect Timeout=60; Min Pool Size=5; Max Pool Size=100;";

The call (I've just forced a rollback immediately after the execute to test it):

Dim Oraclecon As New OracleConnection(_OracleConnection)
Dim sqlCon As New SqlConnection(_SQLConnection)
Dim oTrans As OracleTransaction = Nothing

 Oraclecon.Open()
 oTrans = Oraclecon.BeginTransaction()
 Dim myCMD As New OracleCommand()
 myCMD.Connection = Oraclecon
 myCMD.Transaction = oTrans
 myCMD.CommandText = "CREATE_USER"
 myCMD.CommandType = CommandType.StoredProcedure
 myCMD.Parameters.Add(New OracleParameter("username", OracleType.VarChar)).Value = UserName
 myCMD.Parameters.Add(New OracleParameter("passwd", OracleType.VarChar)).Value = Password
 myCMD.Parameters.Add(New OracleParameter("speed", OracleType.VarChar)).Value = Speed
 myCMD.Parameters.Add(New OracleParameter("monthly_quota", OracleType.VarChar)).Value = Quota
 myCMD.Parameters.Add(New OracleParameter("type", OracleType.VarChar)).Value = "H"
 Dim oparam As OracleParameter
 oparam = New OracleParameter("success_flag", OracleType.VarChar)
 oparam.Size = 1
 oparam.Direction = ParameterDirection.Output

 Dim oparam2 As OracleParameter
 oparam2 = New OracleParameter("err_msg", OracleType.VarChar)
 oparam2.Direction = ParameterDirection.Output
 oparam2.Size = 100

 myCMD.Parameters.Add(oparam)
 myCMD.Parameters.Add(oparam2)

 Dim RowId As OracleString

 myCMD.ExecuteOracleNonQuery(RowId)
 oTrans.Rollback()

I can't give the details of the stored procedure but it does a commit and rollback inside it.

Either way, it is doing an insert, and that immediate rollback does not rollback the insert.

Any ideas?

A: 

Without the source of the package this one could be tricky.

For starters, Oracle normally raises an exception if you do a COMMIT or ROLLBACK inside a package that is not marked with the AUTONOMOUS TRANSACTION pragma, regardless of the calling language.

So I presume that if the package contains a COMMIT and ROLLBACK then the package MUST be autonomous. This would mean that your client side rollback wouldn't have any impact.

If a package is autonomous then an exception is raised if you exit the package without having done either a ROLLBACK or COMMIT - i.e. there should be no route where the package could exit, leaving an uncommitted row on the database.

The only situation that could explain what you are seeing is if the rollback FAILED, or was not executed at all, and the code then carried on to COMMIT.

(Another possible option - the Insert has ALSO been done as an autonomous transaction, with a self-contained commit, so that the data was committed before the rollback?).

Autonomous transactions are notorious for (re)-introducing the problems that atomic transactions are there to avoid (race conditions, data integrity, deadlocks) - they have their uses, but must be handled with care.

JulesLt
@JulesLt, what exception does Oracle raise when you issue a Commit/Rollback in a package without AUTONOMOUS TRANSACTION pragma? I do that all the time without ill effect... see above example, they work with commits and without AUTONOMOUS TRANSACTION pragma. I rewrote the above anomous block into a procedure and it works just fine...
tanging
Hi Jules, I follow what you're saying. How can I identify if it is autonomous (how is it marked). As far as I know, the procedure has one or two basic insert statements -> a commit -> then an exception WHEN OTHERS with a rollback. And one last commit before the end. Does this fall into the case of me not being able to rollback from vb?
Kamal
Tanging - I can't find the error code but it only occurs when you cross language 'levels' in a single transaction - i.e. you have some Java code that updates a row, then calls a procedure, or pl/sql code that calls out to a C extproc that contains a commit. (The second example works fine if you isolate the extproc call inside an autonomous transaction).
JulesLt
Of course I don't know whether VB does / doesn't trigger this kind of error, or if it's one of those daft frameworks that decides to auto-commit after any command.
JulesLt
A: 

The Commit/Rollback logic in the PL/SQL (regardless of AUTONOMOUS TRANSACTION clause --> DO NOT USE THIS unless you are error logging: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2212445691154)

So if you issue a COMMIT in your package your data is commited, PERIOD. Rollback the same way.

Create table for the following examples:

create  table SHOW_TRANSACTION (MISC varchar2(15)) ;
/

Looking at this example shows just that:

declare

   procedure DOTHEINSERT(DATAM in SHOW_TRANSACTION.MISC%type ,
                        doCommit IN boolean

   ) 
   as
   begin    
            insert into SHOW_TRANSACTION (MISC) 
            values (DATAM);

            if DOCOMMIT   then
               commit ;
            else
               ROLLBACK ;
            end if;
   end ;
begin
    DOTHEINSERT('commit1' , true);
    DOTHEINSERT('NOcommit1' , false);
    DOTHEINSERT('commit2' , true);
    DOTHEINSERT('NOcommit2' , false);
    DOTHEINSERT('commit3' , true);
    ROLLBACK;


end ;
/

select * from SHOW_TRANSACTION ;
/
MISC            
--------------- 
commit1         
commit2         
commit3   

Notice the final ROLLBACK does nothing? this is because the COMMIT/ROLLBACKS in the procedure are effecting the entire scope, look at this example:

truncate table SHOW_TRANSACTION; --start with clean slate

declare

   procedure DOTHEINSERT(DATAM in SHOW_TRANSACTION.MISC%type ,
                        doCommit IN boolean

   ) 
   as
   begin    
            insert into SHOW_TRANSACTION (MISC) 
            values (DATAM);

            if DOCOMMIT   then
               commit ;
            --else (HERE I AM GETTING RID OF THE PROCEDURES ROLLBACK, SO EVERYTHING IS BEING ROLLBACK'ED
            --   ROLLBACK ;

            end if;
   end ;
begin
    DOTHEINSERT('commit1' , true);
    DOTHEINSERT('NOcommit1' , false);
    DOTHEINSERT('commit2' , true);
    DOTHEINSERT('NOcommit2' , false);
    DOTHEINSERT('commit3' , true);
    ROLLBACK;


end ;
/

select * from SHOW_TRANSACTION ;
/

MISC            
--------------- 
commit1         
NOcommit1       
commit2         
NOCOMMIT2       
commit3     

Here the package ROLLBACK is removed, so when that COMMIT happens, the contents of all the INSERTS prior to that are inserted.

If you want the VB application to handle the transaction, you must remove the commit/rollback from the PL/SQL.

Also, it does not matter where the commit/rollback are, they are indicative to the ENTIRE SCOPE of all items in the transaction:

truncate table SHOW_TRANSACTION ; 

declare

   procedure DOTHEINSERT(DATAM in SHOW_TRANSACTION.MISC%type ,
                        doCommit IN boolean

   ) 
   as
          procedure DOTHETRANSACTION(doCommit IN boolean) as 
          begin
            if DOCOMMIT   then
               commit ;
            else -- (HERE I AM GETTING RID OF THE PROCEDURES ROLLBACK, SO EVERYTHING IS BEING ROLLBACK'ED
               ROLLBACK ;
            end if;             
          END DOTHETRANSACTION;
   begin    
            insert into SHOW_TRANSACTION (MISC) 
            values (DATAM);
            DOTHETRANSACTION(doCommit);
   end DOTHEINSERT;
begin
    DOTHEINSERT('commit1' , true);
    DOTHEINSERT('NOcommit1' , false);
    DOTHEINSERT('commit2' , true);
    DOTHEINSERT('NOcommit2' , false);
    DOTHEINSERT('commit3' , true);
    ROLLBACK;


end ;
/

select * from SHOW_TRANSACTION ;
/

MISC            
--------------- 
commit1         
COMMIT2         
commit3 

/* now the transaction is in a sub-sub procedure */

tanging
Hi tanging. So basically what I should ask the third party to do is NOT commit or rollback at all. and my vb should control the commit and rollback completely from my side?
Kamal
@Kamal , if you want to handle the transaction scope of the SQL, that is exactly what you need to do. The alternative is ugly in that you can issue the deletes on your own (since they have already commited) - but if they will change it, then by all means do so. (in my .Net applications I leave the transaction scope to the .Net app and do not issue commits/rollbacks form Oracle)
tanging
Thanks for the solution. I don't have a deep Oracle background and wanted a definitive answer rather than me tinkering for hours. Also not having direct access to the s-procs does not help. now the harder question - how to convince a third party vendor to change their code...
Kamal
@Kamal good luck with that one. I would use words like '...instead of it giving me a mysterious rollback without notifying me (which could cause a bug in my application or force me to issue another query just to see if it worked, let the error bubble up to my application and allow me to issue the rollback and do my logic since I will have know the item failed...' also, keep in mind that you will need a try/catch and be on the look out for the exception to handle the rollback
tanging
@Tanging - Thanks. I do have a much more complex set of code with try and catch blocks. I simplified it for the question. Thanks for all your help.
Kamal
@Tanging - an update - can you believe it...they changed their side...in less than 48 hours. Thanks for your help.
Kamal
@Kamal, absolutely magnificent! I wish all third parties operated that quickly!
tanging