Factories
You have already encountered factories before. It’s time to consider themin detail.
Factories are designed to create instances of procedures, functions,or triggers. The factory class must inherit from one of the IUdrProcedureFactory
,IUdrFunctionFactory
or IUdrTriggerFactory
interfaces depending on the UDR type.Instances of these must be registered as UDR entry points in the firebird_udr_plugin
function.
function firebird_udr_plugin(AStatus: IStatus; AUnloadFlagLocal: BooleanPtr;
AUdrPlugin: IUdrPlugin): BooleanPtr; cdecl;
begin
// register our function
AUdrPlugin.registerFunction(AStatus, 'sum_args',
TSumArgsFunctionFactory.Create());
// register our procedure
AUdrPlugin.registerProcedure(AStatus, 'gen_rows', TGenRowsFactory.Create());
// register our trigger
AUdrPlugin.registerTrigger(AStatus, 'test_trigger',
TMyTriggerFactory.Create());
theirUnloadFlag := AUnloadFlagLocal;
Result := @myUnloadFlag;
end;
In this example, the TSumArgsFunctionFactory
class inherits theIUdrFunctionFactory
interface, TGenRowsFactory
inherits theIUdrProcedureFactory
interface, and TMyTriggerFactory
inheritsthe IUdrTriggerFactory
interface.
Factory instances are created and bound to entry points the first time an externalprocedure, function, or trigger is loaded. This happens once per Firebird processcreation. Thus, for the SuperServer architecture, for all connections there will beexactly one factory instance associated with each entry point; for Classic, this numberof instances will be multiplied by the number of connections.
When writing factory classes, you need to implement the setup
and newItem
methodsfrom the IUdrProcedureFactory
, IUdrFunctionFactory
or IUdrTriggerFactory
interfaces.
IUdrFunctionFactory = class(IDisposable)
const VERSION = 3;
procedure setup(status: IStatus; context: IExternalContext;
metadata: IRoutineMetadata; inBuilder: IMetadataBuilder;
outBuilder: IMetadataBuilder);
function newItem(status: IStatus; context: IExternalContext;
metadata: IRoutineMetadata): IExternalFunction;
end;
IUdrProcedureFactory = class(IDisposable)
const VERSION = 3;
procedure setup(status: IStatus; context: IExternalContext;
metadata: IRoutineMetadata; inBuilder: IMetadataBuilder;
outBuilder: IMetadataBuilder);
function newItem(status: IStatus; context: IExternalContext;
metadata: IRoutineMetadata): IExternalProcedure;
end;
IUdrTriggerFactory = class(IDisposable)
const VERSION = 3;
procedure setup(status: IStatus; context: IExternalContext;
metadata: IRoutineMetadata; fieldsBuilder: IMetadataBuilder);
function newItem(status: IStatus; context: IExternalContext;
metadata: IRoutineMetadata): IExternalTrigger;
end;
Also, since these interfaces inherit the IDisposable
interface, you must alsoimplement the dispose
method. This means that Firebird will unload the factory itselfwhen needed. In the dispose
method, you need to place code that releases resourceswhen the factory instance is destroyed. To simplify the implementation of interfacemethods, it is convenient to use the classes IUdrProcedureFactoryImpl
,IUdrFunctionFactoryImpl
, IUdrTriggerFactoryImpl
. Let’s consider each of the methodsin more detail.