Function factory
Now we need to write the factory and the function itself. They will be locatedin the SumArgsFunc
module. Examples for writing procedures and triggers would bepresented later.
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
// the structure to which the input message will be mapped
TSumArgsInMsg = record
n1: Integer;
n1Null: WordBool;
n2: Integer;
n2Null: WordBool;
n3: Integer;
n3Null: WordBool;
end;
PSumArgsInMsg = ^TSumArgsInMsg;
// the structure to which the output message will be mapped
TSumArgsOutMsg = record
result: Integer;
resultNull: WordBool;
end;
PSumArgsOutMsg = ^TSumArgsOutMsg;
// Factory for instantiating the external function TSumArgsFunction
TSumArgsFunctionFactory = class(IUdrFunctionFactoryImpl)
// Called when the factory is destroyed
procedure dispose(); override;
{ Executed each time an external function is loaded into the metadata cache.
Used to change the format of the input and output messages.
@param(AStatus status vector)
@param(AContext External function execution context)
@param(AMetadata External Function Metadata)
@param(AInBuilder Message builder for input metadata)
@param(AOutBuilder Message builder for output metadata)
}
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
{ Creating a new external function instance TSumArgsFunction
@param(AStatus status vector)
@param(AContext External function execution context)
@param(AMetadata External Function Metadata)
@returns(Экземпляр external function)
}
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalFunction; override;
end;
// External function TSumArgsFunction.
TSumArgsFunction = class(IExternalFunctionImpl)
// Called when the function instance is destroyed
procedure dispose(); override;
{ This method is called just before execute and tells
kernel our requested character set to exchange data internally
this method. During this call, the context uses the character set
obtained from ExternalEngine::getCharSet.
@param(AStatus Status vector)
@param(AContext External function execution context)
@param(AName Character set name)
@param(AName Character set name length)
}
procedure getCharSet(AStatus: IStatus; AContext: IExternalContext;
AName: PAnsiChar; ANameSize: Cardinal); override;
{ Executing an external function
@param(AStatus Status vector)
@param(AContext External function execution context)
@param(AInMsg Pointer to input message)
@param(AOutMsg Pointer to output message)
}
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
// convert pointers to input and output to typed ones
xInput := PSumArgsInMsg(AInMsg);
xOutput := PSumArgsOutMsg(AOutMsg);
// by default, the output argument is NULL, so set it to nullFlag
xOutput^.resultNull := True;
// if one of the arguments is NULL, then the result is NULL
// otherwise, we calculate the sum of the arguments
with xInput^ do
begin
if not (n1Null or n2Null or n3Null) then
begin
xOutput^.result := n1 + n2 + n3;
// if there is a result, then reset the NULL flag
xOutput^.resultNull := False;
end;
end;
end;
procedure TSumArgsFunction.getCharSet(AStatus: IStatus;
AContext: IExternalContext; AName: PAnsiChar; ANameSize: Cardinal);
begin
end;
end.
The external function factory must implement the interfaceIUdrFunctionFactory
. To simplify, we simply inherit the classIUdrFunctionFactoryImpl
. Each external function needs its own factory.However, if factories do not have specifics for creating somefunction, then you can write a generic factory using generics. Later we will give anexample of how to do this.
The dispose
method is called when the factory is destroyed, in which we must releasethe previously allocated resources. In this case, we simply call the destructor.
The setup method is executed each time an external function is loaded into the metadatacache. In it, you can do various actions that are necessary before creating an instanceof a function, for example, change the format for input and output messages. We’ll talkabout it in more detail later.
The newItem
method is called to instantiate the external function. This method ispassed a pointer to the status vector, the context of the external function, and themetadata of the external function. With IRoutineMetadata
you can get the format ofthe input and output message, the body of the external function, andother metadata. In this method, you can create different instances of an externalfunction depending on its declaration in PSQL. Metadata can be passed to the createdexternal function instance if needed. In our case, we simply create an instance of anexternal functionTSumArgsFunction
.