views:

36

answers:

2

I have PHP code that execute a stored procedure 10 times. If one stored procedure call fails, it should continue on, and at the end commit the transaction.

It basically looks like this:

$connection = getConn();

foreach($row as $i=>$j) {
  $SQL = "BEGIN MYPROC.EXECUTE(:VAL1, :VAL2); END;";
  $statement = OCIParse($connection, $SQL);

  oci_bind_by_name($statement, 'VAL1', $row[i]['FIRSTVAL']);
  oci_bind_by_name($statement, 'VAL2', $row[i]['SECONDVAL']);

  $success = @OCIExecute($statement, OCI_DEFAULT);
  if(!$success) {
    print 'Exception in stored proc call';
  }
  else {
    print 'Success';
  }

}
oci_commit($connection);

My question is, if there is an exception raised in, say, the 5th stored proc call, will that roll back all the stored proc calls up to that point?

+1  A: 

As long as each procedure is executed in the same session, and none of them issue a commit, then the changes they make can be rolled back. You should open the connection outside the loop, then do all your work within that. As it stands now, you're connecting each time through the loop, which is inefficient and won't allow what you want to do. You should also take the commit statement outside the loop.

Something like this, perhaps:

$SQL = "BEGIN MYPROC.EXECUTE(:VAL1, :VAL2); END;";
$connection = getConn();
$statement = OCIParse($connection, $SQL);

foreach($row as $i=>$j) {

  oci_bind_by_name($statement, 'VAL1', $row[i]['FIRSTVAL']);
  oci_bind_by_name($statement, 'VAL2', $row[i]['SECONDVAL']);

  $success = @OCIExecute($statement, OCI_DEFAULT);
  if(!$success) {
    print 'Exception in stored proc call';
    oci_rollback($connection);
    exit processing here... 
  }
  else {
    print 'Success';
  }
}
oci_commit($connection);
DCookie
Apologies for not being more explicit... the getConn() function returns a singleton connection, so it's all the same connection. Your answer is what I expect, but for some reason all the successful executions are being committed even though the 5th of 10 executions is failing with an unhandled exception.
RenderIn
I edited the code slightly, with the same end result, to be more clear.
RenderIn
Are you rolling back your transaction when the 5th fails? Your commit in the original code is inside your loop, which means each time through the loop your changes are made permanent. You need to put the commit outside the loop, and don't do it at all if you encounter an exception.
DCookie
Also, you don't need to reparse the statement each time through the loop. Simply re-binding the parameter values is sufficient.
DCookie
Your example does not abort processing in the case of an error though. Just because the 5th procedure generates an Oracle exception does not mean all prior updates are rolled back. If you continue processing after the exception, and do a commit, then the successful updates are still in effect and your commit saves them for posterity.
DCookie
@DCookie Sorry, the example I concocted originally was wrong, with the commit inside the loop. The commit in my code is only executed once, after all the looping. My actual code is several pages long but the gist of it is that there is a loop where the application code is effectively eating a database exception thrown in one of the iterations. I was under the impression that once an unhandled exception bubbles outside of the database and into the application code, Oracle rolls back the transaction.
RenderIn
@DCookie Thanks for the information... I was thinking Oracle rolled back transactions automatically when exceptions went unhandled. Typically I am working towards that goal anyway, so I didn't realize that exceptions could resolve up to the application, dealt with there, and that the database would not judge the success of the transaction based on that.
RenderIn
+1  A: 

I think the PHP driver, and not Oracle, is controlling the commit here. This seems to indicate that as of PHP 5.3.2 (PECL OCI8 1.4), each invocation of the OCIExecute (by default) will commit the statement, regardless of what is in the stored procedure.

dpbradley
I would detest that "feature", unless there were an equivalent call that didn't commit. I guess OP will have to create another procedure in the DB that does the loop processing.
DCookie
I use the OCI_DEFAULT option to prevent auto-committing (now called OCI_NO_AUTO_COMMIT in newer versions of PHP).
RenderIn
@DCookie -Yup, it goes against traditional thinking - first ran across this concept when troubleshooting JDBC code where a similar thing was happening.
dpbradley
@Renderln - it sounds like you're doing the right thing with the driver to avoid the commit - I see one of your other comments about swallowing the exception in the application code and can only guess that there's a logic problem in your actual code.
dpbradley
@dpbradley so are we all in agreement that Oracle will not roll back a transaction even if a SQL exception bubbles up to the PHP application code? Given that I have autocommit off, this behavior would be explained by DCookie's answer above.
RenderIn
@Renderln -yes, I think DCookie's answer explains it - the subsequent iterations of the loop are doing the commits.
dpbradley