Фабрика функций
Теперь необходимо написать фабрику и саму функцию. Они будут расположеныв модуле SumArgsFunc
. Примеры для написания процедур и триггеров будутпредставлены позже.
unit SumArgsFunc;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses
Firebird;
{ *********************************************************
create function sum_args (
n1 integer,
n2 integer,
n3 integer
) returns integer
external name 'myudr!sum_args'
engine udr;
********************************************************* }
type
// структура на которое будет отображено входное сообщение
TSumArgsInMsg = record
n1: Integer;
n1Null: WordBool;
n2: Integer;
n2Null: WordBool;
n3: Integer;
n3Null: WordBool;
end;
PSumArgsInMsg = ^TSumArgsInMsg;
// структура на которое будет отображено выходное сообщение
TSumArgsOutMsg = record
result: Integer;
resultNull: WordBool;
end;
PSumArgsOutMsg = ^TSumArgsOutMsg;
// Фабрика для создания экземпляра внешней функции TSumArgsFunction
TSumArgsFunctionFactory = class(IUdrFunctionFactoryImpl)
// Вызывается при уничтожении фабрики
procedure dispose(); override;
{ Выполняется каждый раз при загрузке внешней функции в кеш метаданных.
Используется для изменения формата входного и выходного сообщения.
@param(AStatus Статус вектор)
@param(AContext Контекст выполнения внешней функции)
@param(AMetadata Метаданные внешней функции)
@param(AInBuilder Построитель сообщения для входных метаданных)
@param(AOutBuilder Построитель сообщения для выходных метаданных)
}
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
{ Создание нового экземпляра внешней функции TSumArgsFunction
@param(AStatus Статус вектор)
@param(AContext Контекст выполнения внешней функции)
@param(AMetadata Метаданные внешней функции)
@returns(Экземпляр внешней функции)
}
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalFunction; override;
end;
// Внешняя функция TSumArgsFunction.
TSumArgsFunction = class(IExternalFunctionImpl)
// Вызывается при уничтожении экземпляра функции
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;
{ Выполнение внешней функции
@param(AStatus Статус вектор)
@param(AContext Контекст выполнения внешней функции)
@param(AInMsg Указатель на входное сообщение)
@param(AOutMsg Указатель на выходное сообщение)
}
procedure execute(AStatus: IStatus; AContext: IExternalContext;
AInMsg: Pointer; AOutMsg: Pointer); override;
end;
implementation
{ TSumArgsFunctionFactory }
procedure TSumArgsFunctionFactory.dispose;
begin
Destroy;
end;
function TSumArgsFunctionFactory.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction;
begin
Result := TSumArgsFunction.Create();
end;
procedure TSumArgsFunctionFactory.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TSumArgsFunction }
procedure TSumArgsFunction.dispose;
begin
Destroy;
end;
procedure TSumArgsFunction.execute(AStatus: IStatus; AContext: IExternalContext;
AInMsg, AOutMsg: Pointer);
var
xInput: PSumArgsInMsg;
xOutput: PSumArgsOutMsg;
begin
// преобразовываем указатели на вход и выход к типизированным
xInput := PSumArgsInMsg(AInMsg);
xOutput := PSumArgsOutMsg(AOutMsg);
// по умолчанию выходной аргумент = NULL, а поэтому выставляем ему nullFlag
xOutput^.resultNull := True;
// если один из аргументов NULL значит и результат NULL
// в противном случае считаем сумму аргументов
with xInput^ do
begin
if not (n1Null or n2Null or n3Null) then
begin
xOutput^.result := n1 + n2 + n3;
// раз есть результат, то сбрасываем NULL флаг
xOutput^.resultNull := False;
end;
end;
end;
procedure TSumArgsFunction.getCharSet(AStatus: IStatus;
AContext: IExternalContext; AName: PAnsiChar; ANameSize: Cardinal);
begin
end;
end.
Фабрика внешней функции должна реализовать интерфейсIUdrFunctionFactory
. Для упрощения просто наследуем классIUdrFunctionFactoryImpl
. Для каждой внешней функции нужна своя фабрика.Впрочем, если фабрики не имеют специфики для создания некоторой функции,то можно написать обобщённую фабрику с помощью дженериков. Позже мыприведём пример как это сделать.
Метод dispose
вызывается при уничтожении фабрики, в нём мы должныосвободить ранее выделенные ресурсы. В данном случае просто вызываемдеструктор.
Метод setup выполняется каждый раз при загрузке внешней функции в кешметаданных. В нём можно делать различные действия которые необходимыперед созданием экземпляра функции, например изменить формат для входныхи выходных сообщений. Более подробно поговорим о нём позже.
Метод newItem
вызывается для создания экземпляра внешней функции. В этотметод передаётся указатель на статус вектор, контекст внешней функции иметаданные внешней функции. С помощью IRoutineMetadata
вы можетеполучить формат входного и выходного сообщения, тело внешней функции идругие метаданные. В этом методе вы можете создавать различныеэкземпляры внешней функции в зависимости от её объявления в PSQL.Метаданные можно передать в созданный экземпляр внешней функции если этонеобходимо. В нашем случае мы просто создаём экземпляр внешней функцииTSumArgsFunction
.