Generic factories
In the process of developing UDR, it is necessary for eachexternal procedure, function or trigger to write your factorycreating an instance is UDR. This task can be simplified bywriting generalized factories using the so -called generics. Theyare available starting with Delphi 2009, in Free Pascal startingwith the FPC 2.2 version.
Note
|
Comment
In Free Pascal, the syntax for creating generic types isdifferent from Delphi.Since version FPC 2.6.0 the syntax compatible with Delphi isdeclared. |
Consider the two main cases for which generalized factories willbe written:
-
copies of external procedures, functions and triggers do notrequire any information about metadata, do not need specialactions in the logic of creating UDR copies, fixed structures areused to work with messages;
-
Corps of external procedures,functions and triggers require information about metadata,special actions are not needed in the logic of creating UDRcopies, and instances of messages
IMessagemetadata
are used towork with messages.
In the first case, it is enough to simply create the desired copyof the class in the Newitem
method without additional actions.To do this, we will use the restriction of the designer in theclassrooms of the classes IUdrFunctionFactoryImpl
,IUdrProcedureFactoryImpl
, IUdrTriggerFactoryImpl
. The ads of such factories are as follows:
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
// A simple external function factory
TFunctionSimpleFactory<T: IExternalFunctionImpl, constructor> = class
(IUdrFunctionFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalFunction; override;
end;
// A simple external procedure factory
TProcedureSimpleFactory<T: IExternalProcedureImpl, constructor> = class
(IUdrProcedureFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalProcedure; override;
end;
// A simple external trigger factory
TTriggerSimpleFactory<T: IExternalTriggerImpl, constructor> = class
(IUdrTriggerFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalTrigger; override;
end;
In the implementation section, the body of the setup
method canbe left empty, nothing is done in them, in the body of the`dispose 'method, just call the destructor. And in the body of theNewitem
method, you just need to call the default designer forthe substitution type` t`.
implementation
{ TProcedureSimpleFactory<T> }
procedure TProcedureSimpleFactory<T>.dispose;
begin
Destroy;
end;
function TProcedureSimpleFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalProcedure;
begin
Result := T.Create;
end;
procedure TProcedureSimpleFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TFunctionFactory<T> }
procedure TFunctionSimpleFactory<T>.dispose;
begin
Destroy;
end;
function TFunctionSimpleFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction;
begin
Result := T.Create;
end;
procedure TFunctionSimpleFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TTriggerSimpleFactory<T> }
procedure TTriggerSimpleFactory<T>.dispose;
begin
Destroy;
end;
function TTriggerSimpleFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalTrigger;
begin
Result := T.Create;
end;
procedure TTriggerSimpleFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AFieldsBuilder: IMetadataBuilder);
begin
end;
Now for case 1, you can not write factories for each procedure,function or trigger. Instead, register them with genericfactories as follows:
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr;
AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl;
begin
// register our function
AUdrPlugin.registerFunction(AStatus, 'sum_args',
TFunctionSimpleFactory<TSumArgsFunction>.Create());
// register our procedure
AUdrPlugin.registerProcedure(AStatus, 'gen_rows',
TProcedureSimpleFactory<TGenRowsProcedure>.Create());
// register our trigger
AUdrPlugin.registerTrigger(AStatus, 'test_trigger',
TTriggerSimpleFactory<TMyTrigger>.Create());
theirUnloadFlag := AUnloadFlagLocal;
Result := @myUnloadFlag;
end;
The second case is more complicated. By default, metadatainformation is not transmitted into copies of procedures,functions and triggers. However, metadata is transmitted as aparameter in the method of newitem
factories. UDR metadata hasthe type of IRoutineMetadata
, the life cycle of which iscontrolled by the Firebird engine itself, so it can be safelytransferred to UDR copies. From it you can get copies ofinterfaces for the input and output message, metadata and triggertype, UDR name, package, entrance points and UDR body. Theclasses themselves for the implementation of external procedures,functions and triggers do not have fields for storing metadata,so we will have to make their heirs.
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
...
// External function with metadata
TExternalFunction = class(IExternalFunctionImpl)
Metadata: IRoutineMetadata;
end;
// External procedure with metadata
TExternalProcedure = class(IExternalProcedureImpl)
Metadata: IRoutineMetadata;
end;
// External trigger with metadata
TExternalTrigger = class(IExternalTriggerImpl)
Metadata: IRoutineMetadata;
end;
In this case, your own stored procedures, functions, and triggersshould be inherited from new classes with metadata.
Now let’s declare the factories that will create the UDR andinitialize metadata.
unit UdrFactories;
{$IFDEF FPC}
{$MODE DELPHI}{$H+}
{$ENDIF}
interface
uses SysUtils, Firebird;
type
...
// Factory of external functions with metadata
TFunctionFactory<T: TExternalFunction, constructor> = class
(IUdrFunctionFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalFunction; override;
end;
// Factory of external procedures with metadata
TProcedureFactory<T: TExternalProcedure, constructor> = class
(IUdrProcedureFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalProcedure; override;
end;
// Factory of external triggers with metadata
TTriggerFactory<T: TExternalTrigger, constructor> = class
(IUdrTriggerFactoryImpl)
procedure dispose(); override;
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder); override;
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalTrigger; override;
end;
The implementation of the method newitem
is trivial and issimilar to the first case, except that it is necessary toinitialize the field with metadan.
implementation
...
{ TFunctionFactory<T> }
procedure TFunctionFactory<T>.dispose;
begin
Destroy;
end;
function TFunctionFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction;
begin
Result := T.Create;
(Result as T).Metadata := AMetadata;
end;
procedure TFunctionFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TProcedureFactory<T> }
procedure TProcedureFactory<T>.dispose;
begin
Destroy;
end;
function TProcedureFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalProcedure;
begin
Result := T.Create;
(Result as T).Metadata := AMetadata;
end;
procedure TProcedureFactory<T>.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TTriggerFactory<T> }
procedure TTriggerFactory<T>.dispose;
begin
Destroy;
end;
function TTriggerFactory<T>.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalTrigger;
begin
Result := T.Create;
(Result as T).Metadata := AMetadata;
end;
procedure TTriggerFactory<T>.setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AFieldsBuilder: IMetadataBuilder);
begin
end;
A ready-made module with generic factories can be downloaded athttps://github.com/sim1984/udr-book/blob/master/examples/Common/UdrFactories.pas.