Интересно отметить, что Oracle также трактует анонимный блок PL/SQL как один оператор. Рассмотрим следующую хранимую процедуру:
ops$tkyte@ORA10G> create or replace procedure p2 as 3 begin4 insert into t values ( 1 );
5 insert into t values (-1 );6 end;7 /
Procedure created.
Процедура создана.
ops$tkyte@ORA10G> select * from t;no rows selected
нет выбранных строк
ops$tkyte@ORA10G> select * from t2; CNT
0
Итак, у нас имеется процедура, которая, как мы знаем, завершится неудачей. Второй оператор INSERT в этом случае всегда провалится. Давайте посмотрим, что случится, если мы запустим эту хранимую процедуру:
ops$tkyte@ORA10G> begin2 p;3 end;4 /
Я вставил и обновил 1 строк(у)Я вставил и обновил 1 строк(у)
begin* ERROR at line 1: ORA-02290: check constraint (OPS$TKYTE.SYS_C009598) violatedORA-06512: at "OPS$TKYTE.P", line 5ORA-06512: at line 2
ОШИБКА в строке 1: ORA-02290: нарушение проверочного ограничения целостности (OPS$TKYTE.SYS_C009598) ORA-06512: в "OPS$TKYTE.P", строка 5 ORA-06512: в строке 2
ops$tkyte@ORA10G> select * from t;no rows selected
нет выбранных строк
ops$tkyte@ORA10G> select * from t2; CNT
0
Как видите, СУБД Oracle трактует вызов хранимой процедуры как атомарный оператор. Клиент посылает блок кода BEGIN P; END;, а Oracle обертывает его в SAVEPOINT. Поскольку процедура P терпит неудачу, Oracle восстанавливает базу данных в состояние, которое она имела в точке, непосредственно предшествовавшей ее вызову. Теперь, если слегка изменить блок, будет получен совершенно другой результат:
ops$tkyte@ORA10G> begin2 p;
3 exception4 when others then null;5 end;6 /
Я вставил и обновил 1 строк(у)Я вставил и обновил 1 строк(у)
PL/SQL procedure successfully completed.
Процедура PL/SQL успешно завершена.
ops$tkyte@ORA10G> select * from t;X
1
ops$tkyte@ORA10G> select * from t2; CNT
1
Здесь мы запускаем блок кода, игнорирующий все возможные ошибки и отличия в выводе. В то время как первый вызов P не проявляется ни в каких изменениях, этот INSERT выполняется успешно и столбец CNT в T2 соответствующим образом увеличивается.
На заметку! Я считаю ошибочным практически любой код, который содержит обработчик исключений WHEN OTHERS, но не включает вызова RAISE, чтобы передать исключение выше. Он молча игнорирует ошибку и изменяет семантику транзакций. Перехват WHEN OTHERSи трансляция исключения в код возврата в старом стиле изменяет способ ожидаемого поведения базы данных.
СУБД Oracle считает “оператором” любой блок кода, присланный клиентом. Этот оператор завершается успехом за счет самостоятельного перехвата и игнорирования ошибок, поэтому установка If error then rollback... не вступает в действие и Oracle не откатывает работу к SAVEPOINT. Поэтому частичная работа, выполненная P, сохраняется. Причина того, что эта частичная работа предохраняется, состоит в том, что мы имеем автономность уровня оператора внутри P: каждый оператор внутри P является атомарным. P становится клиентом Oracle, когда подтверждает свои два оператора INSERT. Каждый INSERT либо целиком успешен, либо нет. Это подтверждается тем фактом, что мы можем видеть, что триггер Tбыл инициирован дважды и дважды обновил T2, хотя счетчик в T2 отображает только один UPDATE. Второе выполнение INSERT внутри P снабжено неявным SAVEPOINT, обернутым вокруг него.
Отличие между двумя блоками кода довольно-таки тонкое, и вы должны учитывать его в своих приложениях. Добавление обработчика исключений к блоку кода PL/SQL может радикально изменить поведение. Другой способ закодировать это — восстанавливающий атомарность уровня оператора для всего блока PL/SQL, выглядит следующим образом:
ops$tkyte@ORA10G> begin2 savepoint sp;3 p;4 exception
5 when others then
6 rollback to sp;7 end;8 /
Я вставил и обновил 1 строк(у)Я вставил и обновил 1 строк(у)PL/SQL procedure successfully completed.
Процедура PL/SQL успешно завершена.
ops$tkyte@ORA10G> select * from t;no rows selected
нет выбранных строк
ops$tkyte@ORA10G> select * from t2; CNT
0
Внимание! Предыдущий код представляет собой пример исключительно порочной практики. Вы никогда не должны перехватывать WHEN OTHERS, как и не должны явно кодировать то, что уже обеспечивает Oracle — до тех пор, пока речь идет о семантике транзакции.
Здесь, имитируя то, что Oracle обычно делает для нас с помощью SAVEPOINT, мы можем восстановить оригинальное поведение, перехватывая и “игнорируя” ошибку. Я привел этот пример только для иллюстрации; вообще говоря, это исключительно порочная практика.
| < Предыдущая | Следующая > |
|---|


