PHP 执行 Oracle PL/SQL - OCI8 时何时会执行自动回滚?

发布于 2024-09-06 18:24:28 字数 609 浏览 2 评论 0原文

我有执行存储过程 10 次的 PHP 代码。如果一个存储过程调用失败,它应该继续,并在最后提交事务。

它基本上看起来像这样:

$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);

我的问题是,如果在第五个存储过程调用中引发异常,是否会回滚到该点的所有存储过程调用?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

为你拒绝所有暧昧 2024-09-13 18:24:28

只要每个过程都在同一个会话中执行,并且没有一个过程发出提交,那么它们所做的更改就可以回滚。您应该在循环外打开连接,然后在其中完成所有工作。就目前而言,您每次都通过循环进行连接,这是低效的并且不允许您执行您想做的事情。您还应该将 commit 语句放在循环之外。

也许是这样的:

$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);

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);
痴情 2024-09-13 18:24:28

我认为 PHP 驱动程序(而不是 Oracle)正在控制此处的提交。 似乎表明从 PHP 5.3.2 (PECL OCI8 1.4),每次调用 OCIExecute(默认情况下)都会提交该语句,无论存储过程中有什么。

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.

你不是我要的菜∠ 2024-09-13 18:24:28

最近必须对此进行一些测试。当发生未处理的异常时,Oracle 似乎会部分回滚到同一会话的最顶层包含开始块或提交的点(并不总是一直返回到先前的提交)。给定一个带有 int id 和 varchar2 val 和 proc 的表:

CREATE OR REPLACE PROCEDURE PROC_AUTO_COMMIT_TEST( 
   p_id int, p_val varchar2, p_cmd varchar2
) IS
BEGIN
   if (p_cmd = 'init') then
        delete from TEMP_AUTOCOMMIT_TEST;
        insert into TEMP_AUTOCOMMIT_TEST values(1,'one');
        insert into TEMP_AUTOCOMMIT_TEST values(2,'two');
        insert into TEMP_AUTOCOMMIT_TEST values(3,'three');
        commit;  
   else   
        update TEMP_AUTOCOMMIT_TEST 
           set val = p_val
         where id = p_id;

         if (p_cmd = 'throw') then
            insert into TEMP_AUTOCOMMIT_TEST values(3,'THREE');  -- throws
         end if;
   end if;     
END PROC_AUTO_COMMIT_TEST;

然后执行以下操作:

begin
    PROC_AUTO_COMMIT_TEST(0, null, 'init');   
    begin
        PROC_AUTO_COMMIT_TEST(1, 'ONE', null);
    end;
    begin
        PROC_AUTO_COMMIT_TEST(2, 'TWO', null);
        PROC_AUTO_COMMIT_TEST(3, 'THREE', 'throw');
    end;
end;

一路回滚到“init”内的提交(也回滚了一个)。

与按顺序执行这些操作(从 Toad(自动提交关闭,每个块上按 F9,整个块按 f5)或 Sqlplus,中间有 /):

begin
    PROC_AUTO_COMMIT_TEST(0, null, 'init');   
end;

begin
    PROC_AUTO_COMMIT_TEST(1, 'ONE', null);
end;

begin
    PROC_AUTO_COMMIT_TEST(2, 'TWO', null);
    PROC_AUTO_COMMIT_TEST(3, 'THREE', 'throw');
end;

在 3 个内发生的异常然后回滚到“1”之后。然而,“ONE”仍然需要回滚或提交,因为它持有行锁(使用 TOAD 中的会话浏览器进行验证)。将此称为部分回滚,因为它不会一路返回到“init”调用中的提交并留下锁定的行。我假设这种情况更接近 PHP 和其他连接器可能正在做的事情。

Had to do some testing on this recently. When an unhandled exception occurs it seems Oracle does a partial rollback up to the point of the topmost containing begin block or commit for the same session (not always all the way back to the prior commit). Given a table with int id and varchar2 val and proc:

CREATE OR REPLACE PROCEDURE PROC_AUTO_COMMIT_TEST( 
   p_id int, p_val varchar2, p_cmd varchar2
) IS
BEGIN
   if (p_cmd = 'init') then
        delete from TEMP_AUTOCOMMIT_TEST;
        insert into TEMP_AUTOCOMMIT_TEST values(1,'one');
        insert into TEMP_AUTOCOMMIT_TEST values(2,'two');
        insert into TEMP_AUTOCOMMIT_TEST values(3,'three');
        commit;  
   else   
        update TEMP_AUTOCOMMIT_TEST 
           set val = p_val
         where id = p_id;

         if (p_cmd = 'throw') then
            insert into TEMP_AUTOCOMMIT_TEST values(3,'THREE');  -- throws
         end if;
   end if;     
END PROC_AUTO_COMMIT_TEST;

Then executing this:

begin
    PROC_AUTO_COMMIT_TEST(0, null, 'init');   
    begin
        PROC_AUTO_COMMIT_TEST(1, 'ONE', null);
    end;
    begin
        PROC_AUTO_COMMIT_TEST(2, 'TWO', null);
        PROC_AUTO_COMMIT_TEST(3, 'THREE', 'throw');
    end;
end;

rollsback all the way back to the commit within the 'init' (ONE rolled back as well).

Versus excecuting these in order (from either Toad (autocommit off, F9 on each block, f5 for whole thing) or Sqlplus with /'s in between):

begin
    PROC_AUTO_COMMIT_TEST(0, null, 'init');   
end;

begin
    PROC_AUTO_COMMIT_TEST(1, 'ONE', null);
end;

begin
    PROC_AUTO_COMMIT_TEST(2, 'TWO', null);
    PROC_AUTO_COMMIT_TEST(3, 'THREE', 'throw');
end;

The exception ocurring within THREE then rolls back to just after the 'ONE'. However the 'ONE' still needs to either be rolled back or committed since it is holding a row lock (verified with Session Browser in TOAD). Calling this a partial rollback because it doesn't go all the way back to the commit within the 'init' call and leaves a row locked. I am assuming this case is closer to what PHP might be doing and other connectors.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文