Classe d’un déclencheur
Une classe de déclenchement externe doit implémenter l’interface IUdrTriggerFactory
. Pour simplifier les choses, nous héritons simplement de la classe IUdrTriggerFactoryImpl
. Chaque trigger externe a besoin de sa propre classe.
La méthode dispose
est appelée lors de la destruction de la classe, au cours de laquelle nous devons libérer les ressources précédemment allouées. Dans ce cas, nous appelons simplement le destructeur.
La méthode setup
est exécutée chaque fois qu’un trigger externe est chargé dans le cache de métadonnées. Dans cette méthode, vous pouvez effectuer diverses actions qui sont nécessaires avant de créer une instance de trigger, par exemple, pour changer le format des messages pour les champs de la table. Nous y reviendrons plus en détail ultérieurement.
La méthode newItem
est appelée pour instancier un déclencheur externe. On passe à cette méthode un pointeur sur le vecteur d’état, le contexte du déclencheur externe et les métadonnées du déclencheur externe. Avec IRoutineMetadata
, vous pouvez obtenir le format du message pour les valeurs des champs nouveaux et anciens, le corps du déclencheur externe et d’autres métadonnées. Dans cette méthode, vous pouvez créer différentes instances du déclencheur externe en fonction de sa déclaration dans PSQL. Les métadonnées peuvent être transmises à l’instance de déclencheur externe créée si nécessaire. Dans notre cas, nous instançons simplement le trigger externe TMyTrigger
.
Nous allons placer la classe du déclencheur, ainsi que le déclencheur lui-même, dans le module 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;
}
// La structure d'affichage des messages NEW.* et OLD.* doit correspondre à l'ensemble des champs de la table de 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;
// Classe pour l'instanciation d'un déclencheur externe TMyTrigger
TMyTriggerFactory = class(IUdrTriggerFactoryImpl)
// Appelé lors de la destruction de la classe
procedure dispose(); override;
{ Exécuté chaque fois qu'un déclencheur externe est chargé dans le cache de métadonnées.
Utilisé pour modifier le format du message pour les champs.
@param(AStatus Vecteur de statut)
@param(AContext Contexte d'exécution du déclencheur externe)
@param(AMetadata Métadonnées du déclencheur externe)
@param(AFieldsBuilder Constructeur de messages (`IMessageMetadata`) pour les champs de table)
}
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); override;
{ Création d'une nouvelle instance du déclencheur externe TMyTrigger
@param(AStatus Vecteur de statut)
@param(AContext Contexte d'exécution du déclencheur externe)
@param(AMetadata Métadonnées du déclencheur externe)
@returns(Instance du déclencheur externe)
}
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalTrigger; override;
end;
TMyTrigger = class(IExternalTriggerImpl)
// Appelé lorsque le déclencheur est détruit
procedure dispose(); override;
{ Cette méthode est appelée juste avant l'exécution et indique au noyau le jeu de caractères demandé
pour échanger des données en interne.
cette méthode. Lors de cet appel, le contexte utilise le jeu de caractères
obtenu par ExternalEngine::getCharSet.
@param(AStatus Vecteur de statut)
@param(AContext Contexte d'exécution du déclencheur externe)
@param(AName Nom du jeu de caractères)
@param(ANameSize Longueur du nom du jeu de caractères)
}
procedure getCharSet(AStatus: IStatus; AContext: IExternalContext;
AName: PAnsiChar; ANameSize: Cardinal); override;
{ exécution du déclencheur TMyTrigger
@param(AStatus Vecteur de statut)
@param(AContext Contexte d'exécution du déclencheur externe)
@param(AAction Action du déclencheur (événement en cours))
@param(AOldMsg Message pour les anciennes valeurs de champ :OLD.*)
@param(ANewMsg Message pour les nouvelles valeurs de champ :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.