47. Écrire un gestionnaire de langage procédural
Tous les appels de fonctions écrites dans un langage autre que celui
de l'interface « version 1 »
pour les langages compilés (ce qui inclut les fonctions dans les
langages procéduraux utilisateur, les fonctions SQL et les fonctions
utilisant l'interface de langage compilé version 0), passent par une
fonction spécifique au langage du gestionnaire
d'appels. Le gestionnaire d'appels exécute la fonction de
manière appropriée, par exemple en interprétant le code source
fourni. Ce chapitre décrit l'écriture du gestionnaire d'appels d'un
nouveau langage procédural.
Le gestionnaire d'appel d'un langage procédural est une fonction
« normale » qui doit être écrite
dans un langage compilé tel que le C, en utilisant l'interface
version-1, et enregistrée sous PostgreSQL™ comme une fonction sans
argument et retournant le type language_handler. Ce pseudo-type spécial identifie la
fonction comme gestionnaire d'appel et empêche son appel à partir des
commandes SQL.
L'appel du gestionnaire d'appels est identique à celui de toute autre
fonction : il reçoit un pointeur de structure
FunctionCallInfoData qui contient les valeurs des
arguments et d'autres informations de la fonction appelée. Il
retourne un résultat Datum (et, initialise
le champ
isnull
de la structure
FunctionCallInfoData si un résultat
SQL NULL doit être retourné). La différence entre un gestionnaire
d'appels et une fonction ordinaire se situe au niveau du champ
flinfo->fn_oid
de la
structure FunctionCallInfoData. Dans
le cas du gestionnaire d'appels, il contiendra l'OID de la fonction à
appeler, et non pas celui du gestionnaire d'appels lui-même. Le
gestionnaire d'appels utilise ce champ pour déterminer la fonction à
exécuter. De plus, la liste d'arguments passée a été dressée à partir
de la déclaration de la fonction cible, et non pas en fonction du
gestionnaire d'appels.
C'est le gestionnaire d'appels qui récupère l'entrée de la fonction
dans la table système pg_proc et analyse
les types des arguments et de la valeur de retour de la fonction
appelée. La clause AS de la commande
CREATE FUNCTION
se
situe dans la colonne prosrc de pg_proc. Il s'agit généralement du texte source du
langage procédural lui-même (comme pour PL/Tcl) mais, en théorie,
cela peut être un chemin vers un fichier ou tout ce qui indique au
gestionnaire d'appels les détails des actions à effectuer.
Souvent, la même fonction est appelée plusieurs fois dans la même
instruction SQL. L'utilisation du champ
flinfo->fn_extra
évite au gestionnaire
d'appels de répéter la recherche des informations concernant la
fonction appelée. Ce champ, initialement NULL, peut être configuré par le gestionnaire
d'appels pour pointer sur l'information concernant la fonction
appelée. Lors des appels suivants, si
flinfo->fn_extra
est différent de
NULL, alors il peut être utilisé et
l'étape de recherche d'information évitée. Le gestionnaire d'appels
doit s'assurer que
flinfo->fn_extra
pointe sur une zone
mémoire qui restera allouée au moins jusqu'à la fin de la requête en
cours, car une structure de données FmgrInfo peut être conservée aussi longtemps.
Cela peut-être obtenu par l'allocation des données supplémentaires
dans le contexte mémoire spécifié par
flinfo->fn_mcxt
; de telles données
ont la même espérance de vie que FmgrInfo. Le gestionnaire peut également choisir
d'utiliser un contexte mémoire de plus longue espérance de vie de
façon à mettre en cache sur plusieurs requêtes les informations
concernant les définitions des fonctions.
Lorsqu'une fonction en langage procédural est appelée via un
déclencheur, aucun argument ne lui est passé de façon traditionnelle
mais le champ
context
de
FunctionCallInfoData pointe sur une
structure TriggerData. Il n'est pas
NULL comme c'est le cas dans les appels
de fonctions standard. Un gestionnaire de langage doit fournir les
mécanismes pour que les fonctions de langages procéduraux obtiennent
les informations du déclencheur.
Voici un modèle de gestionnaire de langage procédural écrit en C :
#include "postgres.h"
#include "executor/spi.h"
#include "commands/trigger.h"
#include "fmgr.h"
#include "access/heapam.h"
#include "utils/syscache.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
PG_FUNCTION_INFO_V1(plsample_call_handler);
Datum
plsample_call_handler(PG_FUNCTION_ARGS)
{
Datum retval;
if (CALLED_AS_TRIGGER(fcinfo))
{
/*
* Appelé comme procédure de déclencheur
*/
TriggerData *trigdata = (TriggerData *) fcinfo->context;
retval = ...
}
else
{
/*
* Appelé en tant que fonction
*/
retval = ...
}
return retval;
}
Il suffit de remplacer les points de suspension par quelques milliers
de lignes de codes pour compléter ce modèle.
Lorsque la fonction du gestionnaire est compilée dans un module
chargeable (voir Section 33.9.6,
« Compiler et lier des fonctions chargées
dynamiquement »), les commandes suivantes enregistrent le
langage procédural défini dans l'exemple :
CREATE FUNCTION plsample_call_handler() RETURNS language_handler
AS 'nomfichier'
LANGUAGE C;
CREATE LANGUAGE plsample
HANDLER plsample_call_handler;
Les langages procéduraux inclus dans la distribution standard sont de
bons points de départ à l'écriture de son propre gestionnaire
d'appels. Les sources se trouvent dans le répertoire src/pl.