Trigger Factory
An external trigger factory must implement the IUdrTriggerFactory
interface. Tosimplify things, we simply inherit the IUdrTriggerFactoryImpl
class. Each externaltrigger needs its own factory.
The dispose
method is called when the factory is destroyed, in which we must releasepreviously allocated resources. In this case, we simply call the destructor.
The setup
method is executed every time an external trigger is loaded into themetadata cache. In it, you can do various actions that are necessary before creating atrigger instance, for example, to change the format of messages for table fields. We’lltalk about it in more detail later.
The newItem
method is called to instantiate an external trigger. This method ispassed a pointer to the status vector, the context of the external trigger, and themetadata of the external trigger. With IRoutineMetadata
you can get the messageformat for new and old field values, the body of the external trigger, and othermetadata. In this method, you can create different instances of the external triggerdepending on its declaration in PSQL. Metadata can be passed to the created externaltrigger instance if necessary. In our case, we simply instantiate the external triggerTMyTrigger
.
We will place the trigger factory, as well as the trigger itself, in the TestTrigger
module.
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;
}
// structure for displaying NEW.* and OLD.* messages
// must match the field set of the test table
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;
// Factory for instantiating external trigger TMyTrigger
TMyTriggerFactory = class(IUdrTriggerFactoryImpl)
// Called when the factory is destroyed
procedure dispose(); override;
{ Executed each time an external trigger is loaded into the metadata cache.
Used to change the message format for fields.
@param(AStatus Status vector)
@param(AContext External trigger execution context)
@param(AMetadata External trigger metadata)
@param(AFieldsBuilder Message builder for table fields)
}
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); override;
{ Creating a new instance of the external trigger TMyTrigger
@param(AStatus Status vector)
@param(AContext External trigger execution context)
@param(AMetadata External trigger metadata)
@returns(Instance of external trigger)
}
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalTrigger; override;
end;
TMyTrigger = class(IExternalTriggerImpl)
// Called when the trigger is destroyed
procedure dispose(); override;
{ This method is called just before execute and tells
kernel our requested character set to exchange data internally
this method. During this call, the context uses the character set
obtained from ExternalEngine::getCharSet.
@param(AStatus Status vector)
@param(AContext External trigger execution context)
@param(AName Character set name)
@param(AName Character set name length)
}
procedure getCharSet(AStatus: IStatus; AContext: IExternalContext;
AName: PAnsiChar; ANameSize: Cardinal); override;
{ trigger execution TMyTrigger
@param(AStatus Status vector)
@param(AContext External trigger execution context)
@param(AAction Action (current event) trigger)
@param(AOldMsg Message for old field values :OLD.*)
@param(ANewMsg Message for new field values :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.