Фабрика процедур
Фабрика внешней процедуры должна реализовать интерфейсIUdrProcedureFactory. Для упрощения просто наследуем классIUdrProcedureFactoryImpl. Для каждой внешней процедуры нужна свояфабрика. Впрочем, если фабрики не имеют специфики для создания некоторойпроцедуры, то можно написать обобщённую фабрику с помощью дженериков.Позже мы приведём пример как это сделать.
Метод dispose вызывается при уничтожении фабрики, в нём мы должныосвободить ранее выделенные ресурсы. В данном случае просто вызываемдеструктор.
Метод setup выполняется каждый раз при загрузке внешней процедуры в кешметаданных. В нём можно делать различные действия которые необходимыперед созданием экземпляра процедуры, например изменение формата длявходных и выходных сообщений. Более подробно поговорим о нём позже.
Метод newItem вызывается для создания экземпляра внешней процедуры. Вэтот метод передаётся указатель на статус вектор, контекст внешнейпроцедуры и метаданные внешней процедуры. С помощью IRoutineMetadata выможете получить формат входного и выходного сообщения, тело внешнейфункции и другие метаданные. В этом методе вы можете создавать различныеэкземпляры внешней функции в зависимости от её объявления в PSQL.Метаданные можно передать в созданный экземпляр внешней процедуры еслиэто необходимо. В нашем случае мы просто создаём экземпляр внешнейпроцедуры TSumArgsProcedure.
Фабрику процедуры, а также саму процедуру расположим в модулеSumArgsProc.
unit SumArgsProc;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses
Firebird;
{ **********************************************************
create procedure sp_sum_args (
n1 integer,
n2 integer,
n3 integer
) returns (result integer)
external name 'myudr!sum_args_proc'
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;
// Фабрика для создания экземпляра внешней процедуры TSumArgsProcedure
TSumArgsProcedureFactory = class(IUdrProcedureFactoryImpl)
// Вызывается при уничтожении фабрики
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;
{ Создание нового экземпляра внешней процедуры TSumArgsProcedure
@param(AStatus Статус вектор)
@param(AContext Контекст выполнения внешней процедуры)
@param(AMetadata Метаданные внешней процедуры)
@returns(Экземпляр внешней процедуры)
}
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalProcedure; override;
end;
TSumArgsProcedure = class(IExternalProcedureImpl)
public
// Вызывается при уничтожении экземпляра процедуры
procedure dispose(); override;
{ Этот метод вызывается непосредственно перед open и сообщает
ядру наш запрошенный набор символов для обмена данными внутри
этого метода. Во время этого вызова контекст использует набор символов,
полученный из 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 Указатель на выходное сообщение)
@returns(Набор данных для селективной процедуры или
nil для процедур выполнения)
}
function open(AStatus: IStatus; AContext: IExternalContext; AInMsg: Pointer;
AOutMsg: Pointer): IExternalResultSet; override;
end;
implementation
{ TSumArgsProcedureFactory }
procedure TSumArgsProcedureFactory.dispose;
begin
Destroy;
end;
function TSumArgsProcedureFactory.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalProcedure;
begin
Result := TSumArgsProcedure.create;
end;
procedure TSumArgsProcedureFactory.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata; AInBuilder,
AOutBuilder: IMetadataBuilder);
begin
end;
{ TSumArgsProcedure }
procedure TSumArgsProcedure.dispose;
begin
Destroy;
end;
procedure TSumArgsProcedure.getCharSet(AStatus: IStatus;
AContext: IExternalContext; AName: PAnsiChar; ANameSize: Cardinal);
begin
end;
function TSumArgsProcedure.open(AStatus: IStatus; AContext: IExternalContext;
AInMsg, AOutMsg: Pointer): IExternalResultSet;
var
xInput: PSumArgsInMsg;
xOutput: PSumArgsOutMsg;
begin
// Набор данных для выполняемых процедур возращать не надо
Result := nil;
// преобразовываем указатели на вход и выход к типизированным
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;
end.