Фабрика триггеров
Фабрика внешнего триггера должна реализовать интерфейс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.