Chapitre 47. Écrire un gestionnaire de langage procédural

Tous les appels vers des fonctions écrites dans un langage autre que celui de l'interface << version 1 >> pour les langages compilés (ceci inclut les fonctions dans les langages procéduraux définis par l'utilisateur, les fonctions écrites en SQL et les fonctions utilisant l'interface de langage compilé version 0), passent par une fonction du gestionnaire d'appels spécifique au langage. Il est de la responsabilité du gestionnaire d'appels d'exécuter la fonction de manière significative, comme par exemple en interprétant le texte source fourni. Ce chapitre indique comment le gestionnaire d'appels d'un nouveau langage procédural peut être écrit.

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é avec PostgreSQL comme ne prenant aucun argument et retournant le type language_handler. Ce pseudo-type spécial identifie la fonction en tant que gestionnaire d'appel et l'empêche d'être appelée directement à partir des commandes SQL.

Le gestionnaire d'appels est appelé de la même façon que n'importe quelle autre fonction : il reçoit un pointeur vers une structure FunctionCallInfoData contenant des valeurs en argument et une information sur la fonction appelée. Il doit renvoyer un résultat Datum (et, si possible, initialiser le champ isnull de la structure FunctionCallInfoData s'il souhaite renvoyer un résultat SQL NULL). La différence entre un gestionnaire d'appels et une fonction ordinaire est que le champ flinfo->fn_oid de la structure FunctionCallInfoData contiendra l'OID de la fonction à appeler, et non pas celui du gestionnaire d'appels lui-même. Le gestionnaire d'appels doit utiliser ce champ pour déterminer la fonction à exécuter. De plus, la liste d'arguments passée a été configurée en accord avec la déclaration de la fonction cible, et non pas en fonction du gestionnaire d'appels.

C'est au gestionnaire d'appels de récupérer l'entrée de la fonction à partir de la table système pg_proc et d'analyser les types des arguments et de la valeur de retour de la fonction appelée. La clause AS du CREATE FUNCTION de la fonction sera trouvée dans la colonne prosrc de pg_proc. Cela peut être le texte du source du langage procédural lui-même (comme pour PL/Tcl), un chemin vers un fichier ou tout autre chose qui indique en détails ce que le gestionnaire d'appels doit faire.

Souvent, la même fonction est appelée plusieurs fois par instruction SQL. Un gestionnaire d'appels peut éviter des recherches d'informations répétées sur la fonction appelée en utilisant le champ flinfo->fn_extra. Il sera initialement à NULL mais pourra être configuré par le gestionnaire d'appels pour pointer vers l'information de la fonction appelée. Pour les appels suivants, si flinfo->fn_extra est déjà différent de NULL, alors il peut être utilisé et l'étape de recherche d'information pourra être é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 il se peut qu'une structure de données FmgrInfo soit conservée aussi longtemps. Une façon de le faire est d'allouer les données supplémentaires dans le contexte mémoire spécifié par flinfo->fn_mcxt ; de telles données auront normalement la même espérance de vie que FmgrInfo lui-même. Mais le gestionnaire pourrait également choisir d'utiliser un contexte mémoire de plus longue durée de façon à mettre en cache sur plusieurs requêtes des informations sur 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 habituelle mais le champ context de FunctionCallInfoData pointe sur une structure TriggerData, et n'est pas NULL comme c'est le cas dans les appels de fonctions standard. Le gestionnaire d'un langage devrait fournir des mécanismes pour que les fonctions de langages procéduraux obtiennent des 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é en tant que procédure d'un déclencheur
         */
        TriggerData    *trigdata = (TriggerData *) fcinfo->context;

        retval = ...
    }
    else
    {
        /*
         * Appelé en tant que fonction
         */

        retval = ...
    }

    return retval;
}

Seules quelques milliers de lignes de codes devront être ajoutées à la place des points pour compléter ce modèle.

Après avoir compilé la fonction du gestionnaire dans un module chargeable (voir Section 33.7.6), 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;