Création d’instances d’UDR en fonction de leur déclaration
Dans la méthode newItem
, vous pouvez créer différentes instances d’une procédure ou d’une fonction externe, en fonction de sa déclaration dans PSQL. Pour ce faire, vous pouvez utiliser les informations obtenues à partir de IMessageMetadata
.
Supposons que nous voulions implémenter un package PSQL avec le même ensemble de fonctions externes pour élever un nombre au carré pour différents types de données et un point d’entrée unique.
SET TERM ^ ;
CREATE OR ALTER PACKAGE MYUDR2
AS
BEGIN
FUNCTION SqrSmallint(AInput SMALLINT) RETURNS INTEGER;
FUNCTION SqrInteger(AInput INTEGER) RETURNS BIGINT;
FUNCTION SqrBigint(AInput BIGINT) RETURNS BIGINT;
FUNCTION SqrFloat(AInput FLOAT) RETURNS DOUBLE PRECISION;
FUNCTION SqrDouble(AInput DOUBLE PRECISION) RETURNS DOUBLE PRECISION;
END^
RECREATE PACKAGE BODY MYUDR2
AS
BEGIN
FUNCTION SqrSmallint(AInput SMALLINT) RETURNS INTEGER
EXTERNAL NAME 'myudr2!sqrt_func'
ENGINE UDR;
FUNCTION SqrInteger(AInput INTEGER) RETURNS BIGINT
EXTERNAL NAME 'myudr2!sqrt_func'
ENGINE UDR;
FUNCTION SqrBigint(AInput BIGINT) RETURNS BIGINT
EXTERNAL NAME 'myudr2!sqrt_func'
ENGINE UDR;
FUNCTION SqrFloat(AInput FLOAT) RETURNS DOUBLE PRECISION
EXTERNAL NAME 'myudr2!sqrt_func'
ENGINE UDR;
FUNCTION SqrDouble(AInput DOUBLE PRECISION) RETURNS DOUBLE PRECISION
EXTERNAL NAME 'myudr2!sqrt_func'
ENGINE UDR;
END
^
SET TERM ; ^
Pour tester les fonctions, nous utiliserons la requête suivante
SELECT
myudr2.SqrSmallint(1) as n1,
myudr2.SqrInteger(2) as n2,
myudr2.SqrBigint(3) as n3,
myudr2.SqrFloat(3.1) as n4,
myudr2.SqrDouble(3.2) as n5
FROM rdb$database
Pour faciliter le travail avec IMessageMetadata
et les tampons, vous pouvez écrire un wrapper pratique ou essayer d’utiliser IMessageMetadata
et des structures pour afficher les messages ensemble. Ici, nous allons montrer l’utilisation de la seconde méthode.
L’implémentation de cette idée est assez simple : dans la classe de fonctions, nous allons créer différentes instances de fonctions en fonction du type de l’argument d’entrée.Dans les versions modernes de Delphi, vous pouvez utiliser les génériques pour généraliser le code.
.......................
type
// la structure à laquelle le message d'entrée sera associé
TSqrInMsg<T> = record
n1: T;
n1Null: WordBool;
end;
// la structure à laquelle le message de sortie sera associé
TSqrOutMsg<T> = record
result: T;
resultNull: WordBool;
end;
// Classe pour l'instanciation d'une fonction externe TSqrFunction
TSqrFunctionFactory = class(IUdrFunctionFactoryImpl)
// Appelé lors de la destruction de la classe
procedure dispose(); override;
{ Exécuté chaque fois qu'une fonction externe est chargée dans le cache de métadonnées.
Permet de modifier le format des messages d'entrée et de sortie.
@param(AStatus Vecteur de statut)
@param(AContext Contexte d'exécution de la fonction externe)
@param(AMetadata Métadonnées de la fonction externe)
@param(AInBuilder Constructeur de messages pour les métadonnées d'entrée)
@param(AOutBuilder Constructeur de messages pour les métadonnées de sortie)
}
procedure setup(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata; AInBuilder: IMetadataBuilder;
AOutBuilder: IMetadataBuilder); override;
{ Création d'une nouvelle instance d'une TSqrFunction externe
@param(AStatus Vecteur de statut)
@param(AContext Contexte d'exécution de la fonction externe)
@param(AMetadata Métadonnées de la fonction externe)
@returns(Instance de fonction externe)
}
function newItem(AStatus: IStatus; AContext: IExternalContext;
AMetadata: IRoutineMetadata): IExternalFunction; override;
end;
// Fonction externe TSqrFunction.
TSqrFunction<TIn, TOut> = class(IExternalFunctionImpl)
private
function sqrExec(AIn: TIn): TOut; virtual; abstract;
public
type
TInput = TSqrInMsg<TIn>;
TOutput = TSqrOutMsg<TOut>;
PInput = ^TInput;
POutput = ^TOutput;
// Appelé lorsque l'instance de la fonction est détruite
procedure dispose(); override;
{ Cette méthode est appelée juste avant l'exécution et indique au noyau le jeu de caractères requis
pour communiquer dans cette méthode.
Lors de cet appel, le contexte utilise le jeu de caractères obtenu par ExternalEngine::getCharSet.
@param(AStatus Vecteur de statut)
@param(AContext Contexte d'exécution de la fonction externe)
@param(AName Nom du jeu de caractères)
@param(ANameSize Longueur du nom du jeu de caractères)
}
procedure getCharSet(AStatus: IStatus; AContext: IExternalContext;
AName: PAnsiChar; ANameSize: Cardinal); override;
{ Exécution d'une fonction externe
@param(AStatus Vecteur de statut)
@param(AContext Contexte d'exécution de la fonction externe)
@param(AInMsg Pointeur vers le message d'entrée)
@param(AOutMsg Pointeur vers le message de sortie)
}
procedure execute(AStatus: IStatus; AContext: IExternalContext;
AInMsg: Pointer; AOutMsg: Pointer); override;
end;
TSqrExecSmallint = class(TSqrFunction<Smallint, Integer>)
public
function sqrExec(AIn: Smallint): Integer; override;
end;
TSqrExecInteger = class(TSqrFunction<Integer, Int64>)
public
function sqrExec(AIn: Integer): Int64; override;
end;
TSqrExecInt64 = class(TSqrFunction<Int64, Int64>)
public
function sqrExec(AIn: Int64): Int64; override;
end;
TSqrExecFloat = class(TSqrFunction<Single, Double>)
public
function sqrExec(AIn: Single): Double; override;
end;
TSqrExecDouble = class(TSqrFunction<Double, Double>)
public
function sqrExec(AIn: Double): Double; override;
end;
implementation
uses
SysUtils, FbTypes, System.TypInfo;
{ TSqrFunctionFactory }
procedure TSqrFunctionFactory.dispose;
begin
Destroy;
end;
function TSqrFunctionFactory.newItem(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata): IExternalFunction;
var
xInputMetadata: IMessageMetadata;
xInputType: TFBType;
begin
// obtenir le type de l'argument d'entrée
xInputMetadata := AMetadata.getInputMetadata(AStatus);
xInputType := TFBType(xInputMetadata.getType(AStatus, 0));
xInputMetadata.release;
// créer une instance d'une fonction en fonction du type
case xInputType of
SQL_SHORT:
result := TSqrExecSmallint.Create();
SQL_LONG:
result := TSqrExecInteger.Create();
SQL_INT64:
result := TSqrExecInt64.Create();
SQL_FLOAT:
result := TSqrExecFloat.Create();
SQL_DOUBLE, SQL_D_FLOAT:
result := TSqrExecDouble.Create();
else
result := TSqrExecInt64.Create();
end;
end;
procedure TSqrFunctionFactory.setup(AStatus: IStatus;
AContext: IExternalContext; AMetadata: IRoutineMetadata;
AInBuilder, AOutBuilder: IMetadataBuilder);
begin
end;
{ TSqrFunction }
procedure TSqrFunction<TIn, TOut>.dispose;
begin
Destroy;
end;
procedure TSqrFunction<TIn, TOut>.execute(AStatus: IStatus;
AContext: IExternalContext; AInMsg, AOutMsg: Pointer);
var
xInput: PInput;
xOutput: POutput;
begin
xInput := PInput(AInMsg);
xOutput := POutput(AOutMsg);
xOutput.resultNull := True;
if (not xInput.n1Null) then
begin
xOutput.resultNull := False;
xOutput.result := Self.sqrExec(xInput.n1);
end;
end;
procedure TSqrFunction<TIn, TOut>.getCharSet(AStatus: IStatus;
AContext: IExternalContext; AName: PAnsiChar; ANameSize: Cardinal);
begin
end;
{ TSqrtExecSmallint }
function TSqrExecSmallint.sqrExec(AIn: Smallint): Integer;
begin
Result := AIn * AIn;
end;
{ TSqrExecInteger }
function TSqrExecInteger.sqrExec(AIn: Integer): Int64;
begin
Result := AIn * AIn;
end;
{ TSqrExecInt64 }
function TSqrExecInt64.sqrExec(AIn: Int64): Int64;
begin
Result := AIn * AIn;
end;
{ TSqrExecFloat }
function TSqrExecFloat.sqrExec(AIn: Single): Double;
begin
Result := AIn * AIn;
end;
{ TSqrExecDouble }
function TSqrExecDouble.sqrExec(AIn: Double): Double;
begin
Result := AIn * AIn;
end;
.................