Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
Шрифт:
Теперь вернемся к нашей процедуре DELETE_EMPLOYEE. В главе 30, когда эта процедура сталкивалась с ситуацией, где удаляемый служащий имеет заказы в картотеке, она выдавала пользовательское исключение REASSIGN_SALES и просто прекращала работу, отменяла выполненные действия и посылала сообщение исключения работающему с ней человеку.
Однако мы можем заставить процедуру обработать эту ситуацию и позволить процедуре завершиться. Например, обработчик может обнулить ключ SALES_REP и послать сообщение другой процедуре, которая создаст записи в таблице протокола
Мы начинаем с создания таблицы протокола:
SET TERM ^;
CREATE TABLE EMPLOYEE_LOG (
EMP_NO SMALLINT,
TABLE_AFFECTED CHAR(31),
FIELD_AFFECTED CHAR(31),
FIELD_VALUE VARCHAR(20),
USER_NAME VARCHAR(31),
DATESTAMP TIMESTAMP) ^
COMMIT ^
Затем нам нужно создать процедуру, которая будет выполнять протоколирование. Она будет достаточно общей, потому что мы полагаем, что подобная процедура протоколирования может понадобиться и для других задач в этой системе:
CREATE PROCEDURE LOG_ACTION (
EMP_NO SMALLINT,
TABLE_AFFECTED CHAR(31),
FIELD_AFFECTED CHAR(31),
FIELD_VALUE VARCHAR (20))
AS
BEGIN
INSERT INTO EMPLOYEE_LOG
VALUES (:EMP_NO, :TABLE_AFFECTED, :FIELD_AFFECTED,
:FIELD_VALUE, CURRENT_USER, CURRENT_TIMESTAMP) ;
END ^
Последнее, что нужно сделать, - это добавить код обработки исключения в нашу процедуру DELETE_EMPLOYEE:
RECREATE PROCEDURE DELETE_EMPLOYEE (
:emp_num INTEGER)
AS
DECLARE VARIABLE PO_NUMBER CHAR(8);
BEGIN
IF (EXISTS (SELECT PO_NUMBER FROM SALES
WHERE SALES_REP = : emporium)) THEN
EXCEPTION reassign_sales;
В этом месте, если появится исключение, последующие операторы не будут выполняться, и управление будет передано на первый оператор WHEN, который может обрабатывать это исключение.
UPDATE department ...
SET ...
. . .
. . .
DELETE FROM employee
WHERE emp_no = :emp_num;
Вот блок обработчика. Первым делом он в цикле просматривает таблицу SALES и устанавливает SALES_REP в NULL во всех строках, где появляется код нашего удаляемого служащего. В каждой итерации этого цикла он вызывает процедуру протоколирования, передавая ей код служащего вместе с информацией о записи SALES:
WHEN EXCEPTION REASSIGN_SALES DO
BEGIN
FOR SELECT PO_NUMBER FROM SALES
WHERE SALES_REP = :emp_num
INTO :PO_NUMBER
AS CURSOR С
DO
BEGIN
UPDATE SALES SET SALES_REP = NULL
WHERE CURRENT OF C;
EXECUTE PROCEDURE LOG_ACTION (
emp_num, ' SALES ', ' POJSIUMBER', : PO_NUMBER) ;
END
После
EXECUTE PROCEDURE DELETE?EMPLOYEEl (:emp_num) ;
END
END^
COMMIT ^
Протокол ошибок
Если для вас важно сохранять протокол ошибок, помните, что исключения, возникающие у клиента, приводят к отмене всей работы, выполненной в модуле. Если вы ведете протокол в таблице базы данных, то записи протокола исчезнут вместе с другой отмененной работой. В случаях, когда обработчики исправят или "проглотят" каждую ошибку, внутренняя таблица будет работать просто прекрасно.
Если вам нужен протокол, который будет сохраняться и после необработанного исключения, используйте внешнюю таблицу. Подробности см. в разд. "Использование внешних файлов в качестве таблиц" главы 16.
! ! !
СОВЕТ. В конце этой главы описано применение подобной техники в процедуре, которая добавляет строки во внешнюю таблицу, хотя последняя не является таблицей протоколирования ошибок.
. ! .
В версии 1.5 и выше вы можете перехватить числовой код ошибки, который передается внутренне определенному исключению в контекстной переменной SQLCODE или GDSCODE. Это предоставляет весьма компактный способ протоколирования текущего исключения в виде фрагмента вашей подпрограммы обработки исключений.
Внутренне определенные исключения имеют и SQLCODE, и GDSCODE. Ваш код может обратиться только к одному коду, другой будет недоступен.
! ! !
ПРИМЕЧАНИЕ. Каждый раз, когда вы попытаетесь обратиться к этим кодам вне блока обработчика, вы получите ноль.
. ! .
Следующая структура блока кода завершается группой обработчиков исключений. Первые два обрабатывают ошибки SQLCODE в виде пользовательских исключений. Эти пользовательские исключения могут быть обработаны во внешнем блоке или их назначением может быть аварийное завершение процедуры и возврат клиенту полезного сообщения.
Если не появилось ни одного из предусмотренных исключений, а было некоторое другое, то его перехватит оператор WHEN ANY. Его обработчик вызывает хранимую процедуру для вывода записи протокола, передавая код SQLCODE вместе с другими входными аргументами, полученными из контекста блока:
BEGIN
. . .
WHEN SQLCODE -802 DO
EXCEPTION E_EXCEPTION_1;
WHEN SQLCODE -803 DO
EXCEPTION E_EXCEPTION_2;
WHEN ANY DO
EXECUTE PROCEDURE P_ANY_EXCEPTION (SQLCODE, другие входные данные ...);