Подписка обработчика на триггер |
Подписка на триггер производится по следующиму синтаксису:
<подписка-на-триггер> = handler [ with replace ] <имя-обработчика> on trigger <имя-таблицы> <вызов-обработчика> <вид-операции> [ [<приоритет>] ] [ action { <код-на-языке-VIP> } ] [ rollback { <код-на-языке-VIP> } ]
Конструкция with replace означает, что при компиляции обработчика новая версия данного обработчика перекрывает старую при условии, что задано прежнее имя обработчика.
Если ключевая конструкция with replace отсутствует в описании обработчика, то при повторной компиляции будет выдано сообщение об ошибке "Уже есть обработчик с таким именем".
<имя-обработчика> - любой допустимый идентификатор Атлантиса.
Имя обработчика должено быть уникальным во ВСЕЙ системе!
<имя-таблицы> - имя таблицы, на которую подписывается обработчик. Таблица должна быть описана в словаре.
<вызов-обработчика> - обработчик вызывается до или после операции над записью в БД.
<вызов-обработчика> = before | after
before - обработчик вызывается до операции над записью в БД.
after - обработчик вызывается после операции над записью в БД.
<вид-операции> - для каких операций над записью в БД вызывается обработчик.
<вид-операции> = insert | update | delete
insert - обработчик вызывается при операции добавления записи в БД.
update - обработчик вызывается при операции модификации записи в БД.
delete - обработчик вызывается при операции удаления записи из БД.
<приоритет> - целое число в квадратных скобках, задает приоритет при выполнении обработчиков одноименных событий. Меньшее число подразумевает больший приоритет. Внутри одного приоритета обработчики выполняются в произвольно порядке. При откате обработки выполнение обработчиков с меньшим приоритетом не производится, для уже выполнившихся вызывается секция rollback. Максимальное значение приоритета 255, значение по умолчание 123.
Секция action содержит код обработчика события.
Секция rollback описывает действие, если таковое будет, которое предназначено для отката действий, произведенных в теле основного обработчика. Данная секция вызывается, для всех успешно отработавших обработчиков данного сообщения, в порядке обратном прямому.
<код-на-языке-VIP> - операторы . Код должен возвращать true, если код отработал успешно, и false, если нужно "откатить" действие.
Доступны параметры, глобальные и локальные переменные и глобальные костанты. Непосредственно в теле триггера нельзя описать логическую таблицу, недоступна навигация по таблицам БД и операции модификации БД.
Однако можно создать за пределами триггера VIP-объект, реализующий запросы к БД на чтение и модификацию. В триггере объявляется соответствующая объектная переменная и через нее вызваются необходимые методы VIP-объекта. См. Пример 2 этого раздела.
Также для модификации БД в триггере можно воспользоваться специальными функциями GetTableBufferP и SetTableBuffer.
Необходимо учитывать, что повторное изменение может привести к повторному срабатыванию триггера, т.е. возникнет рекурсия. В этом случае разработчик должен предусмотреть выход из нее. Для контроля за вложенностью вызовов можно использовать функцию RecursionLevel.
При модификации каждой записи триггер срабатывает один раз. На практике это может приводить к замедлению работы при обработке больших объемов данных, особенно в тех случаях, когда множественная модификация уходила на сервер БД. Естественно, при наличии триггера множественная модификация разобьется на совокупность одиночных, с вызовами триггера. При разработке триггеров необходимо учитывать этот факт.
Если в триггере вызывается функция, которая приводит к модификации БД необходимо учитывать, что повторное изменение может привести к повторному срабатыванию триггера, т.е. возникнет рекурсия, предусмотреть выход из которой - задача разработчика.
Если триггер срабатывает на таблицу, которая в данный момент времени загружена в память, то пытаться считывать данные из БД, ожидая увидеть там обновленную информацию не стоит. Алгоритм может скидывать изменения в БД только в самом конце. При разработке триггера нужно учитывать эту особенность.
Операция над записью откатывается только для before-триггеров. Однако откатка after-триггеров позволяет прервать выполнение триггеров с меньшим приоритетом, инициировав вызов секций rollback для уже отработавших триггеров.
При откате операция модификации возвращает специальный код возврата: tsOperationAbortedByTrigger.
Триггера не содержат параметров, для доступа к контексту выполнения можно использовать следующие функции:
Пример 1
handler ib1 on trigger X$USERS before insert [99] action { var buf : record as table X$USERS; Message('X$USERS before insert'); GetTableBuffer(buf); Message('X$USERS.xu$loginname = ' + buf.xu$loginname); buf.title := 'IVANOV'; SetTableBuffer(buf); result := true; } rollback { Message('X$USERS before insert rollback'); } handler ib2 on trigger X$USERS before insert [100] action { var buf : record as table X$USERS; GetTableBuffer(buf); Message('X$USERS.xu$loginname = ' + buf.xu$loginname); result := true; }
Данный триггер вызывается при добавлении записи в таблицу HozOper. В триггере производится изменение текущей записи таблицы HozOper и добавление новой записи в таблицу HozForm.
//====================================================== //объявление объектного интерфейса objInterface objTrig; function DoInsert(cRec : comp) : word; end; //объявление Vip-интерфейса vipInterface vipTrig implements objTrig; //====================================================== //реализация Vip-интерфейса interface vipTrig; //------------------------------------------------------ //логическая таблица для связанной таблицы create view as select * from HozForm; //------------------------------------------------------ //функция добавления записи в связанную таблицу function DoInsert(cRec : comp) : word; { ClearBuffer(#HozForm); HozForm.cNRec := cRec; // связка с родительской таблицей (HozOper) HozForm.Formula := 'Новая формула'; result := insert current HozForm; } //------------------------------------------------------ handleEvent cmOnVipUnload: message('dddd'); end; end. //====================================================== // Для модификации текущей записи лучше сделать trigger before insert. // Он выполняется перед добавлением записи в таблицу HozOper. // Установка значений выполняется с использованием GetTableBufferP и SetTableBuffer. handler with replace Trig_Before on trigger HozOper before insert action { //переменная буфера с позицией var buf : record as table HozOper with x$position; GetTableBufferP(buf); // Считываем буфер buf.Name2 := 'Новое значение'; // Изменяем значение поля SetTableBuffer(buf); // Сохраняем буфер result := true; } //====================================================== // Для вставки новых записей лучше сделать trigger after insert. // Триггер выполняется после добавления записи в родительскую таблицу(HozOper). // К моменту вызова триггера HozOper.NRec уже определен. // Переменную объекта лучше сделать на уровне приложения, чтобы // при каждом срабатывании триггера объект каждый раз не создавался. // При первом обращении к VIP-объекту произойдет автоматическая // инициализация. Освобождение произойдет при выходе из приложения var GlobVipTrig : vipTrig new; handler with replace Trig_After on trigger HozOper after insert action { var buf : record as table HozOper; GetTableBuffer(buf); // считываем старый буфер GlobVipTrig.doInsert(buf.NRec); // добавляем запись в таблицу HozForm result := true; }