Мир InterBase


Исключения


Первой из рассматриваемых особенностей языка хранимых процедур (ХП) и триггеров InterBase является возможность использовать "исктючения"

Исключения InterBase во многом похожи на исключения других языков высокого уровня, однако имеют свои особенности Фактически исключение InterBase - это сообщение об ошибке, которое имеет собственное, задаваемое программистом имя и текст сообщения об ошибке. Создается исключение следующим образом:

СРЕАТЕ EXCEPTION <имя_исключения> <текст_исключения>;

Например, мы можем создать исключение такого вида:

CREATE EXCEPTION test_except 'Test exception';

Исключение легко удалить или изменить - удаление совершается командой DROP EXCEPTION <имя_удаляемого_исключения>, а изменение - ALTER EXCEPTION <имя_исключения> <текст_исключения>

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

EXCEPTION <имя_исключения>;

Давайте рассмотрим применение исключений на простом примере хранимой процедуры, выполняющей деление одного числа на другое и возвращающей результат Нам необходимо отследить случай деления на нуль и возбудить исключение, если делитель равен нулю.

Для нашего примера создадим следующее исключение:

CREATE EXCEPTION zero_divide 'Cannot divide by zero!';

Создадим хранимую процедуру, использующую это исключение:

CREATE PROCEDURE SP_DIVIDE (



DELIMOE DOUBLE PRECISION,

DELITEL DOUBLE PRECISION)

RETURNS (

RESULT DOUBLE PRECISION) 

AS 

BEGIN

if (Delitel<0.0000001) then

BEGIN

EXCEPTION zero_divide; 

Result=0; 

END 

ELSE 

BEGIN

Result=Delimoe/Delitel; 

END

SUSPEND; 

END

Как видите, текст ХП тривиален - на входе получаем Delitel и Delimoe, затем сравниваем Delitel с 0.0000001, т. е. фактически с нулем, в пределах выбранной погрешности в одну десятимиллионную (так как вещественные числа невозможно непосредственно сравнивать из-за погрешностей в дробной части). Если Delitel близок к нулю в пределах выбранной погрешности, то мы возбуждаем исключение zero_divide. Что же происходит в случае возникновения исключения? Если мы попробуем вызвать исключение, выполняя процедуру SP_divide с нулевым делителем в isql. то получим следующее


SQL> select * from sp_divide(300,0);

RESULT

==========

Statement failed, SQLCODE = -836

exception 1

-Cannot divide by zero!

Если мы вызовем эту ХП с нулевым делителем в каком-либо другом приложении, то скорее всего получим сообщение об ошибке следующего вида:



Рис 1.4. Сообщение о возникновении исключения

Другими словами, сообщение об ошибке - это результат обработки нашего исключения сервером InterBase. Когда InterBase обнаруживает возникшее в ХП или триггере исключение он прерывает работу этой хранимой процедуры и откатывает все изменения, сделанные в текущем блоке BEGIN END, причем если ХП является процедурой-выборкой, то отменяются действия лишь до последнего оператора SUSPEND.

Это значит, что если в процедуре-выборке есть цикл, в котором производятся какие-то действия, и в теле цикла есть SUSPEND, то при возбуждении исключения О1меня1ся все дейс1вия, выполненные в этом цикле до последнего оператора SUSPEND.

Надо сказать, что в исключениях было бы мало пользы, если бы у разработчика СУБД не было возможности обработать их на уровне базы данных. Чтобы разработчик смог обработать возникшее исключение, применяется следующая конструкция языка XП и триггеров:

WHEN EXCEPTION <имя_исключения> DO 

BEGIN 

/*обработка исключения*/

END


Использование этой конструкции помогает избежать возвращения стандартной ошибки с текстом исключения и произвести собственные действия по обработке исключения.

Когда возбуждается исключение, происходит следующее: выполнение хранимой процедуры (или триггера) прерывается. InterBase начинает искать конструкцию WHEN EXCEPTION...DO для обработки возникшего исключения в текущем блоке BEGIN...END. Если не находит, то поднимается на уровень выше (выше в том смысле, если имеются вложенные блоки BEGIN...END или когда одна ХП вызвана из другой) и ищет обработчик исключения там и т. д., пока либо не найдет подходящий обработчик исключения, либо не закончится вложенность уровней хранимой процедуры. Если обработчик исключения так и не был найден, то возвращается стандартное сообщение об ошибке, включающее текст исключения. Если обработчик найден, то выполняются действия в его блоке BEGIN...END и управление передается на первый оператор, следующий за END обработчика.



Давайте рассмотрим пример обработки исключения, возбуждаемого в нашей процедуре SP_DIVIDE. Предположим, что мы имеем некоторую внешнюю процедуру SP_divide_all, вызывающую SPJDFVIDE для того, чтобы поделить два числа. Конечно, пример сильно утрирован, но он позволяет пояснить способ и смысл использования исключений.

Итак, вот текст нашей хранимой процедуры:

CREATE PROCEDURE sp_test_except(Delltel DOUBLE PRECISION)

RETURNS (rslt DOUBLE PRECISION, status VARCHAR(SO))

AS

BEGIN

Status='Everything is Ok';

SELECT result FROM sp_divide(12,:Delitel) INTO :rslt;

SUSPEND;

WHEN EXCEPTION Zero_divide DO 

BEGIN

Status='zero value found!'; 

rslt=-l; 

SUSPEND; 

END 

END

Эта процедура вызывает процедуру SP_DIVIDE. Если параметр Delitel не равен нулю, то процедура SP_DIVIDE выполняется без проблем и в возвращаемое значение rslt помещается частное от деления, а статусная переменная status принимает значение 'Everything is Ok'. В случае, если возникла исключительная ситуация деления на нуль, то результирующая переменная rslt будет равна -1, а в переменной status будет содержаться сообщение об ошибке - 'zero value found!'. Разумеется, в обработчике исключения можно произвести и более сложную обработку, например записать некорректные данные в особую таблицу или попытаться изменить данные для выполнения операции и вновь попробовать ее выполнить и т. д.


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