в ходе выполнения команд обработчика инициировано новое исключение (такое бывает, например, если в обработчике ошибки регистрируются в специальной таблице, а для нее кончилось место), прилетевшее исходное исключение тогда получает статус «обработано».
Два последних исхода работы обработчика предполагают, что из блока даже с подходящим обработчиком исключение бросится дальше – либо то же самое (после вызова команды RAISE), либо уже другое. Могло прилететь пользовательское исключение, обработаться со своей ошибкой, поэтому из обработчика бросится и в родительском блоке полетит дальше уже системное исключение, как в примере с ошибкой добавления строки в специальную таблицу журнала ошибок.
Примеры обработки исключений
Рассмотрим примеры полетов исключений в программе из трех вложенных блоков:
BEGIN – начало блока1
команда1_блока1;
команда2_блока1;
команда3_блока1;
BEGIN – начало блока2
команда1_блока2;
команда2_блока2 l_int := 1/TO_NUMBER(l_var) (l_var='1' или '0' или 'a')
команда3_блока2;
EXCEPTION
WHEN ZERO_DIVIDE THEN
команда1_zero_блока2;
команда2_zero_блока2;
END; – конец блока2
команда4_блока1;
команда5_блока1;
EXCEPTION
WHEN ZERO_DIVIDE THEN
команда1_zero_блока1;
команда2_zero_блока1;
WHEN OTHERS THEN
команда1_others_блока1;
команда2_others_блока1;
END; – конец блока 1
Пусть команда2_блока2 имеет вид l_int:=1/TO_NUMBER(l_var); где l_int – целочисленная переменная, l_var – символьная.
l_var='1' (без исключений)
l_var='0' (zero divide)
l_var='a' (conversion error)
команда1_блока1;
команда2_блока1;
команда3_блока1;
команда1_блока2;
команда2_блока2;
команда3_блока2;
команда4_блока1;
команда5_блока1;
команда1_блока1;
команда2_блока1;
команда3_блока1;
команда1_блока2;
команда2_блока2(error);
– в блоке 2
– ловится ZERO_DIVIDE:
команда1_zero_блока2;
команда2_zero_блока2;
–продолжение блока 1:
команда4_блока1;
команда5_блока1;
команда1_блока1;
команда2_блока1;
команда3_блока1;
команда1_блока2;
команда2_блока2(error);
–в блоке 2 ошибка
–преобразования
–не ловится, т.к.
–там только ZERO_DIVIDE
– в блоке 1
– ZERO_DIVIDE
– второй раз не ловит,
– а ловит OTHERS
– (он же все ловит):
команда1_others_блока1;
команда2_others_блока1;
Рассмотрим три случая в зависимости от значения, которое принимает переменная l_var ('1', или '0', или 'a').
Когда l_var=1 (первый столбец таблицы) исключения не инициируются выполняются все команды из разделов выполнения в той последовательности, как они записаны в коде.
В случае ошибки деления на ноль (второй столбец таблицы, l_var='0') в команде2_блока2 выполнение блока 2 прекращается, все остальные команды в блоке 2 после нее не выполняются, управление передается в раздел EXCEPTION блока 2, где пытаются поймать исключение деления на ноль (ZERO_DIVIDE). Подходящий обработчик в разделе обработки исключений блока 2 есть, поэтому исключение ловится в блоке 2, в котором успешно выполняются команды обработчика. После успешной обработки продолжается выполнение команд блока 1, родительского для блока 2, в котором произошла обработка исключения.
В случае ошибки преобразования символа к числу (третий столбец таблицы, l_var='a') исключение ошибки преобразования не ловится в разделе EXCEPTION блока 2 и PL/SQL передает управление в родительский блок 1, сразу после END блока 2 и исключение пытаются поймать в разделе EXCEPTION блока 1. В разделе обработки исключений блока 1 есть два обработчика (ZERO_DIVIDE и OTHERS). «Примерка» обработчиков к прилетевшему исключению начинается в той последовательности, как они записаны в коде (сверху вниз). ZERO_DIVIDE для этого исключения не подходит при «примерке» уже второй раз, а OTHERS-обработчик ловит все исключения, поэтому управление передается ему и выполняются две его команды. После успешного выполнения команд обработчика исключение получает статус «обработано».
Передача исключений в вызывающую среду
При разработке клиентских приложений при любом обращении к базе данных нужно предусмотреть обработку ошибок, которые могут произойти как при вызове хранимых программ PL/SQL, так и при выполнении предложений SQL. В коде клиентских программ для этого следует использовать конструкции try/catch, имеющиеся в Java и C, или try/except – в Python.
Если в ходе работы программы PL/SQL произошло так никем и не обработанное исключение, то оно вылетит «наружу», то есть будет передано вызывавшей среде. Например, в SQL*Plus или в прикладное клиентское приложение. В SQL*Plus это выглядит вот так:
Без вылета исключения «наружу»
Читать дальше