Максим Доценко (Maxim Docenko) maxdoc@mail.ru
Начиная с Firebird 1.5 можно заменить текст exception прямо при его вызове. См.
exception.
Предлагаемое решение основано на следующих двух фактах:
- Все операции (insert, delete, update, execute procedure) являются атомарными. Если из-за выполнения одного из вышеперечисленных операторов 100 раз запустятся триггеры, а на 101 раз какой либо из триггеров вызовет EXCEPTION, то все изменения внесенные как самим оператором, так и сработавшими триггерами, автоматически откатятся. Т. е. все вернется к тому состоянию, которое было до вызова этого оператора.
- EXCEPTION хранятся в системной таблице rdb$exceptions, где поле rdb$exception_name (char(31)) содержит соответственно наименование, а rdb$message (varchar(78)) текст ошибки выдаваемой клиенту. Как видим, есть одно ограничение – сообщение не может содержать больше чем 78 символов.
Примечание. Кроме этой проблемы основная проблема при работе с русскоязычными exception состоит в том, что поле rdb$message имеет кодировку none. Как результат, записать русский текст нормально можно, только если соединение имеет кодировку none. В случае кодировки 1251 также возможны проблемы с чтением такого exception.
Берем два этих общеизвестных факта, складываем их и получаем:
Если в триггере или хранимой процедуре изменить сообщение раннее созданного объекта EXCEPTION (например, ERR) следующей командой:
UPDATE rdb$exceptions
SET rdb$message= 'Рта-Ля-Ля'
WHERE rdb$exception_name='ERR';
а затем сразу вызвать его:
EXCEPTION ERR;
то клиент получит исключение с текстом 'Рта-Ля-Ля', а сервер вернет текст сообщения ERR в состояние, предшествующее нашим изменениям. Вот и все!!!
Правда вполне может возникнуть ситуация когда ошибка произойдет одновременно у нескольких клиентов. В этой ситуации кто-то из клиентов получит исключение deadlock. Но ничем – кроме того, что они получат невразумительное сообщение об ошибке это не грозит – все равно все изменения откатятся, да и вероятность данного события в большинстве разработок невелика...
Для удобства все эти действия можно оформить следующим образом:
/* Exceptions definitions */
CREATE EXCEPTION ERR '???';
/* SP Definitions */
CREATE PROCEDURE ERROR(E VARCHAR(2000))
AS
declare variable EE varchar(78);
/*максимально возможный размер для текста ошибки*/
BEGIN
EE='';
EE=:E;
/*если входной параметр больше чем 78 то тут возникает ошибка
которая успешно обрабатывается ниже*/
UPDATE rdb$exceptions
SET rdb$message=:E
WHERE rdb$exception_name='ERR';
EXCEPTION ERR;
/*сюда попадаем только если входной параметр Е больше 78 символов*/
/*EE - содержит 78 символов в обрезанных от E*/
when any do
begin
UPDATE rdb$exceptions
SET rdb$message=:EE
WHERE rdb$exception_name='ERR';
EXCEPTION ERR;
end
END
Как видим в процедуру ERROR можно передавать сообщение, которое состоит больше чем из 78 символов. Оно обрежется до необходимых 78 (используется метод описанный
http://www.volny.cz/iprenosil/interbase/ip_ib_code.htm).
Теперь приведу пару примеров, как это использовать.
Пример использования в триггере:
if (new.id=new.parent) then
execute procedure error('Ссылка на самого себя не допустима !!!');
/*формирование текста на лету */
select id,name from goods where id=new.id into :id,:name;
if (id is not null) then
begin
e='Номер '||id||' уже используется товаром :"'||name||'"';
execute procedure error(e);
end