Фабрика триггеров
Фабрика внешнего триггера должна реализовать интерфейсIUdrTriggerFactory
. Для упрощения просто наследуем классIUdrTriggerFactoryImpl
. Для каждого внешнего триггера нужна свояфабрика.
Метод dispose
вызывается при уничтожении фабрики, в нём мы должныосвободить ранее выделенные ресурсы. В данном случае просто вызываемдеструктор.
Метод setup
выполняется каждый раз при загрузке внешнего триггера в кешметаданных. В нём можно делать различные действия которые необходимыперед созданием экземпляра триггера, например для изменения форматасообщений для полей таблицы. Более подробно поговорим о нём позже.
Метод newItem
вызывается для создания экземпляра внешнего триггера. Вэтот метод передаётся указатель на статус вектор, контекст внешнеготриггера и метаданные внешнего триггера. С помощью IRoutineMetadata
выможете получить формат сообщения для новых и старых значений полей, теловнешнего триггера и другие метаданные. В этом методе вы можете создаватьразличные экземпляры внешнего триггера в зависимости от его объявления вPSQL. Метаданные можно передать в созданный экземпляр внешнего триггераесли это необходимо. В нашем случае мы просто создаём экземпляр внешнеготриггера TMyTrigger
.
Фабрику триггера, а также сам триггер расположим в модуле TestTrigger
.
unit TestTrigger;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses
Firebird, SysUtils;
type
{ **********************************************************
create table test (
id int generated by default as identity,
a int,
b int,
name varchar(100),
constraint pk_test primary key(id)
);
create or alter trigger tr_test_biu for test
active before insert or update position 0
external name 'myudr!test_trigger'
engine udr;
}
// структура для отображения сообщений NEW.* и OLD.*
// должна соответствовать набору полей таблицы test
TFieldsMessage = record
Id: Integer;
IdNull: WordBool;
A: Integer;
ANull: WordBool;
B: Integer;
BNull: WordBool;
Name: record
Length: Word;
Value: array [0 .. 399] of AnsiChar;
end;
NameNull: WordBool;
end;
PFieldsMessage = ^TFieldsMessage;
// Фабрика для создания экземпляра внешнего триггера TMyTrigger
TMyTriggerFactory = class(IUdrTriggerFactoryImpl)
// Вызывается при уничтожении фабрики
procedure dispose(); override;
{ Выполняется каждый раз при загрузке внешнего триггера в кеш метаданных.
Используется для изменения формата сообщений для полей.
@param(AStatus Статус вектор)
@param(AContext Контекст выполнения внешнего триггера)
@param(AMetadata Метаданные внешнего триггера)
@param(AFieldsBuilder Построитель сообщения для полей таблицы)
}
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); override;
{ Создание нового экземпляра внешнего триггера TMyTrigger
@param(AStatus Статус вектор)
@param(AContext Контекст выполнения внешнего триггера)
@param(AMetadata Метаданные внешнего триггера)
@returns(Экземпляр внешнего триггера)
}
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalTrigger; override;
end;
TMyTrigger = class(IExternalTriggerImpl)
// Вызывается при уничтожении триггера
procedure dispose(); override;
{ Этот метод вызывается непосредственно перед execute и сообщает
ядру наш запрошенный набор символов для обмена данными внутри
этого метода. Во время этого вызова контекст использует набор символов,
полученный из ExternalEngine::getCharSet.
@param(AStatus Статус вектор)
@param(AContext Контекст выполнения внешнего триггера)
@param(AName Имя набора символов)
@param(AName Длина имени набора символов)
}
procedure getCharSet(AStatus: IStatus; AContext: IExternalContext;
AName: PAnsiChar; ANameSize: Cardinal); override;
{ выполнение триггера TMyTrigger
@param(AStatus Статус вектор)
@param(AContext Контекст выполнения внешнего триггера)
@param(AAction Действие (текущее событие) триггера)
@param(AOldMsg Сообщение для старых значение полей :OLD.*)
@param(ANewMsg Сообщение для новых значение полей :NEW.*)
}
procedure execute(AStatus: IStatus; AContext: IExternalContext;
AAction: Cardinal; AOldMsg: Pointer; ANewMsg: Pointer); override;
end;
implementation
{ TMyTriggerFactory }
procedure TMyTriggerFactory.dispose;
begin
Destroy;
end;
function TMyTriggerFactory.newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalTrigger;
begin
Result := TMyTrigger.create;
end;
procedure TMyTriggerFactory.setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder);
begin
end;
{ TMyTrigger }
procedure TMyTrigger.dispose;
begin
Destroy;
end;
procedure TMyTrigger.execute(AStatus: IStatus; AContext: IExternalContext;
AAction: Cardinal; AOldMsg, ANewMsg: Pointer);
var
xOld, xNew: PFieldsMessage;
begin
// xOld := PFieldsMessage(AOldMsg);
xNew := PFieldsMessage(ANewMsg);
case AAction of
IExternalTrigger.ACTION_INSERT:
begin
if xNew.BNull and not xNew.ANull then
begin
xNew.B := xNew.A + 1;
xNew.BNull := False;
end;
end;
IExternalTrigger.ACTION_UPDATE:
begin
if xNew.BNull and not xNew.ANull then
begin
xNew.B := xNew.A + 1;
xNew.BNull := False;
end;
end;
IExternalTrigger.ACTION_DELETE:
begin
end;
end;
end;
procedure TMyTrigger.getCharSet(AStatus: IStatus; AContext: IExternalContext;
AName: PAnsiChar; ANameSize: Cardinal);
begin
end;
end.