Динамическое формирование сообщения EXCEPTION в процедурах и триггерах


Предлагаемое решение основано на следующих двух фактах:

1. Все операции (insert, delete, update, execute procedure) являются атомарными.

Если из-за выполнения одного из вышеперечисленных операторов 100 раз запустятся триггеры, а на 101 раз какой либо из триггеров вызовет EXCEPTION ,то все изменения внесенные как самим оператором, так и сработавшими триггерами, автоматически откатятся. Т.е. все вернется к тому состоянию, которое было до вызова этого оператора.

2. 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

Максим Доценко (Maxim Docenko) maxdoc@mail.ru