Базы данных Oracle - статьи

         

Исключения


Как говорилось выше, Oracle в процессе возникновения взаимной блокировки откатывает один из SQL-операторов к неявной точке сохранения, сделанной перед этим оператором. Это справедливо для отдельных SQL-команд. Команда же, помещённая в PL/SQL блок, подчиняется правилам обработки исключений в PL/SQL коде. Если происходит необработанное исключение, то управление передается внешней среде. Чтобы прояснить данную ситуацию, попробуем её смоделировать. Для этого нам надо немного изменить процедуру p1:

ZH@XE(31)> CREATE OR REPLACE PROCEDURE zh.p1(v1 in integer,  v2 in integer, v3 in VARCHAR2)         2> AS         3> BEGIN         4>   INSERT INTO t1 (c1) VALUES (v1);         5>   UPDATE t1 SET c2 = v3 WHERE c1 = v2;         6> END;   Процедура изменена

Теперь процедура  не только изменяет строку, указанную во входных параметрах, но и вставляет ещё одну новую строку. Причём вставка происходит до обновления.

Повторим все те же действия, что и в предыдущем примере с учётом вставки. Для начала в первом сеансе вставим  третью строку  и изменим первую:

ZH@XE(31)> EXECUTE p1(3, 1, 'Строка1');

PL/SQL procedure successfully completed

Посмотрим, занеслась ли строка в таблицу t1:

ZH@XE(31)> SELECT * FROM t1   C1 C2      -- ------- 3          1  Строка1 2  Строка2   Выбрано: 3 строки

Всё нормально. Во втором сеансе вставим четвёртую строку и обновим  вторую:

ZH@XE(24)> EXECUTE p1(4, 2, 'Строка2');   PL/SQL procedure successfully completed

Вернёмся в первый сеанс и вставим пятую строку с изменением второй.

ZH@XE(31)> EXECUTE p1(5, 2, 'Строка2');

Ожидание…

Возникло ожидание. Теперь создадим взаимную блокировку, изменив во втором сеансе первую строку:

ZH@XE(24)> EXECUTE p1(6, 1, 'Строка1');   PL/SQL procedure successfully completed

В первом сеансе возникает ошибка:



ZH@XE(31)> EXECUTE p1(5, 2, 'Строка2');   BEGIN * Ошибка в строке 1: ORA-00060: deadlock detected while waiting for resource ORA-06512: at "ZH.P1", line 5 ORA-06512: at line 2


Выведем в этом же сеансе содержимое таблицы t1:

ZH@XE(31)> SELECT * FROM t1   C1 C2      -- ------- 3          1  Строка1 2  Строка2   Выбрано: 3 строки

Вместо четырёх строк мы видим только три. Где же строка со значением первичного ключа, равным пяти? Ведь ошибка взаимной блокировки должна отменить только последний оператор. Всё так и было бы в случае с отдельными SQL-командами. Но операторы у нас находятся в PL/SQL-объекте, и, как говорилось ранее,  обработка исключений (а ошибка взаимной блокировки вызывает именно исключение) происходит по определённым правилам. В нашем случае в нашей процедуре не установлен обработчик исключений, и поэтому ошибка взаимой блокировки вызывает необработанное исключение в PL/SQL блоке, что приводит к немедленной передаче управления во внешнюю среду с отменой всех незафиксированных изменений, сделанных  в пределах этого блока.

Попробуем ввести в нашу процедуру обработчик исключительных ситуаций, немного изменив её:

ZH@XE(31)> CREATE OR REPLACE PROCEDURE zh.p1(v1 in integer,  v2 in integer, v3 in VARCHAR2)         2> AS         3> BEGIN         4>   INSERT INTO t1 (c1) VALUES (v1);         5>   BEGIN         6>     UPDATE t1 SET c2 = v3 WHERE c1 = v2;         7>   EXCEPTION when others THEN NULL;         8>   END;           9> END;   Процедура изменена

Теперь ошибка взаимного блокирования, происшедшая при выполнении команды UPDATE, не будет приводить к прерыванию процедуры и откату всех сделанных в ней изменений. Убедимся в этом:

Первый сеанс:

ZH@XE(31)> EXECUTE p1(3, 1, 'Строка1');   PL/SQL procedure successfully completed

Второй сеанс:

ZH@XE(24)> EXECUTE p1(4, 2, 'Строка2');   PL/SQL procedure successfully completed

Первый сеанс:

ZH@XE(31)> EXECUTE p1(5, 2, 'Строка2');   PL/SQL procedure successfully completed

Второй сеанс:

ZH@XE(24)> EXECUTE p1(6, 1, 'Строка1');   PL/SQL procedure successfully completed

Как мы видим, хотя ошибка взаимной блокировки и возникла, она не привела к прерыванию выполнения процедуры в первом сеансе.  Также не были отменены изменения, сделанные в этой процедуре до выполнения оператора, вызвавшего исключения:

ZH@XE(31)> SELECT * FROM t1   C1 C2      -- ------- 3          5          1  Строка1 2  Строка2   Выбрано: 4 строки

Какой вывод можно сделать из всего вышесказанного? Никогда не стоит пренебрегать  обработкой исключительной ситуации взаимного блокирования в хранимом PL/SQL объекте. Потерянные изменения могут быть больше, чем при выполнении отдельных SQL-команд.


Содержание раздела