DECLARE
l_amount INTEGER := -100;
l_crncy VARCHAR2(3) := 'RUR';
ex_negative_payment_amount EXCEPTION;
ex_non_rur_payment EXCEPTION;
BEGIN
IF l_amount < 0 THEN
DBMS_OUTPUT.PUT_LINE('Ошибка: сумма отрицательная: '||l_amount);
ELSE
– второй уровень вложенности IF
IF l_crncy <> 'RUR' THEN
DBMS_OUTPUT.PUT_LINE('Ошибка: платеж не в рублях');
ELSE
– потом будет третий уровень вложенности IF
… наконец все проверки пройдены, обрабатываем платеж
END IF;
END IF;
END;
Такой код труднее сопровождать и поддерживать, особенно если логика обработки распределена по многим вложенным вызовам процедур и функций. В этом случае пришлось бы использовать переменные-флаги, передавать и анализировать при каждом вызове коды завершения и т. д.
Посмотрим на поведение диагностических функций при инициировании пользовательских исключений:
SQL> DECLARE
2 exception1 EXCEPTION;
3 BEGIN
4 RAISE exception1;
5 EXCEPTION
6 WHEN OTHERS THEN
7 DBMS_OUTPUT.PUT_LINE('SQLCODE print: '||SQLCODE);
8 DBMS_OUTPUT.PUT_LINE('SQLERRM print: '||SQLERRM);
9 END;
10 /
SQLCODE print: 1
SQLERRM print: User-Defined Exception
PL/SQL procedure successfully completed.
Видно, что пользовательские исключения имеют код ошибки 1, а сообщение об ошибке малоинформативно. Поэтому при работе над кодом нужно следить, чтобы пользовательские исключения обрабатывались соответствующими им обработчиками, а не OTHERS-обработчиком, внутри которого нельзя узнать, какое именно пользовательское исключение прилетело. И уж тем более пользовательские исключения PL/SQL не должны вылетать «наружу» в вызывающую среду.
Процедура RAISE_APPLICATION_ERROR
Если пользовательское исключение все-таки вылетит «наружу» в вызывающую среду, то независимо от того, какое исключение вылетело, «снаружи» выглядеть это будет одинаково – как ошибка ORA-06510.
Исключение exception1
Исключение exception2
SQL> DECLARE
2 exception1 EXCEPTION;
3 BEGIN
4 RAISE exception1;
5 END;
6 /
DECLARE
*
ERROR at line 1:
ORA-06510: PL/SQL:
unhandled user-defined exception
ORA-06512: at line 4
SQL> DECLARE
2 exception2 EXCEPTION;
3 BEGIN
4 RAISE exception2;
5 END;
6 /
DECLARE
*
ERROR at line 1:
ORA-06510: PL/SQL:
unhandled user-defined exception
ORA-06512: at line 4
С системными исключениями дело обстоит иначе – разные системные исключения вылетают «наружу» с разными кодами и сообщениями, что позволяет их обрабатывать в вызывающей среде, так как по коду понятно, какая ошибка произошла.
Как отмечалось выше, системные исключения автоматически инициируются виртуальной машиной PL/SQL при возникновении программных ошибок этапа выполнения. В то же время есть возможность инициировать системные исключения и вручную. Для этого используется процедура RAISE_APPLICATION_ERROR, которая инициирует системные исключения с задаваемыми программистом сообщениями и номерами ошибок из диапазона [-20999,-20000].
SQL> DECLARE
2 l_error_number INTEGER := -20187;
3 l_error_message VARCHAR2(100) := 'Отрицательная сумма платежа';
4 l_amount INTEGER := -10;
5 BEGIN
6 IF l_amount < 0 THEN
7 RAISE_APPLICATION_ERROR(l_error_number,l_error_message);
8 END IF;
9 END;
10 /
DECLARE
*
ERROR at line 1:
ORA-20187: Отрицательная сумма платежа
ORA-06512: at line 7
Привязка пользовательских исключений к ошибкам
В рассмотренных примерах работы с исключениями часто использовались предопределенные исключения, например, исключение ZERO_DIVIDE, которое соответствует ошибке ORA-01476 Divisor is equal to zero. В PL/SQL есть возможность практически к любой ошибке сервера привязать пользовательское исключение и ловить ошибки по именам таких исключений, а не в OTHERS-обработчике.
Для привязки ошибки к пользовательскому исключению достаточно узнать номер ошибки и записать соответствующую директиву компилятору:
PRAGMA EXCEPTION_INIT (имя пользовательского исключения, номер ошибки)
Обработаем ошибку преобразования символьного значения в дату по заданной маске с использованием привязанного исключения:
– сначала специально получаем ошибку и узнаем ее номер,
– это вспомогательный шаг
SQL> DECLARE
2 v1 DATE;
3 BEGIN
4 v1:=TO_DATE('abc','dd.mm.yyyy'); – ожидается не abc, а дата по маске
5 END;
6 /
DECLARE
*
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
ORA-06512: at line 4
– объявляем исключение и привязываем его к ORA-01858
SQL> DECLARE
2 v1 DATE;
3 to_date_convert_error EXCEPTION;
4 PRAGMA EXCEPTION_INIT (to_date_convert_error, -01858);
5 BEGIN
6 v1:=TO_DATE('abc','dd.mm.yyyy');
7 EXCEPTION
8 WHEN to_date_convert_error THEN
9 DBMS_OUTPUT.PUT_LINE('Ошибка преобразования строки в дату');
10 END;
11 /
Ошибка преобразования строки в дату
PL/SQL procedure successfully completed.
Если бы возможности привязки к ошибкам пользовательских исключений не было, то пришлось бы все ошибки обрабатывать в OTHERS-обработчике примерно так:
BEGIN
v1:=TO_DATE('abc','dd.mm.yyyy');
Читать дальше