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