Когда в PHP будут выполняться автоматические откаты при выполнении Oracle PL / SQL - OCI8? - PullRequest
1 голос
/ 22 июня 2010

У меня есть PHP-код, который выполняет хранимую процедуру 10 раз. В случае сбоя одного вызова хранимой процедуры он должен продолжиться и в конце зафиксировать транзакцию.

В основном это выглядит так:

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

У меня вопрос: возникнет ли исключение, скажем, в 5-м хранимом вызове proc, откатит ли он все сохраненные вызовы proc до этого момента?

Ответы [ 3 ]

1 голос
/ 22 июня 2010

Я думаю, что драйвер PHP, а не Oracle, контролирует коммит здесь. Это , по-видимому, указывает на то, что начиная с PHP 5.3.2 (PECL OCI8 1.4) каждый вызов OCIExecute (по умолчанию) будет фиксировать инструкцию независимо от того, что находится в хранимой процедуре.

1 голос
/ 03 февраля 2011

Недавно пришлось провести некоторые тесты. Когда возникает необработанное исключение, кажется, что 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' (ОДИН откат тоже).

По сравнению с их превышением по порядку (из либо Жабы (автоматическая фиксация выключена, 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;

Исключение, возникающее в ТРИ, затем возвращается к «ОДИН». Однако «ONE» все еще необходимо откатить или зафиксировать, поскольку он удерживает блокировку строки (проверено с помощью браузера сеансов в TOAD). Называя это частичным откатом, потому что он не полностью возвращается к фиксации в вызове 'init' и оставляет строку заблокированной. Я предполагаю, что этот случай ближе к тому, что может делать PHP и другие коннекторы.

1 голос
/ 22 июня 2010

Пока каждая процедура выполняется в одном и том же сеансе, и ни одна из них не выполняет фиксацию, изменения, которые они вносят, можно откатить. Вы должны открыть соединение вне цикла, а затем выполнить всю свою работу в этом. В настоящее время вы подключаетесь каждый раз через цикл, который неэффективен и не позволяет выполнять то, что вы хотите сделать. Вы также должны взять оператор 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);
...