[Trad] [svn:pgfr] r1351 - in traduc/tags: . tv840rc1
admin at listes.postgresql.fr
admin at listes.postgresql.fr
Mar 23 Juin 18:25:46 CEST 2009
Author: gleu
Date: 2009-06-23 18:25:46 +0200 (Tue, 23 Jun 2009)
New Revision: 1351
Added:
traduc/tags/tv840rc1/
traduc/tags/tv840rc1/nls.xml
traduc/tags/tv840rc1/xfunc.xml
Removed:
traduc/tags/tv840rc1/nls.xml
traduc/tags/tv840rc1/xfunc.xml
Log:
Ajout du tag de la 8.4 RC 1.
Property changes on: traduc/tags/tv840rc1
___________________________________________________________________
Added: svn:mergeinfo
+
Deleted: traduc/tags/tv840rc1/nls.xml
===================================================================
--- traduc/trunk/postgresql/nls.xml 2009-06-22 07:28:54 UTC (rev 1349)
+++ traduc/tags/tv840rc1/nls.xml 2009-06-23 16:25:46 UTC (rev 1351)
@@ -1,533 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-15"?>
-<!-- Dernière modification
- le $Date$
- par $Author$
- révision $Revision$ -->
-
-<chapter id="nls">
- <chapterinfo>
- <author>
- <firstname>Peter</firstname>
- <surname>Eisentraut</surname>
- </author>
- </chapterinfo>
-
- <title>Support natif des langues</title>
-
- <sect1 id="nls-translator">
- <title>Pour le traducteur</title>
-
- <para>
- Les programmes <productname>PostgreSQL</productname> (serveur et client) peuvent afficher
- leur message dans la langue préférée de l'utilisateur — si les messages ont été traduits.
- Créer et maintenir les ensembles de messages traduits nécessite l'aide de
- personnes parlant leur propre langue et souhaitant contribuer à
- <productname>PostgreSQL</productname>. Il n'est nul besoin d'être un développeur
- pour cela. Cette section explique comment apporter son aide.
- </para>
-
- <sect2>
- <title>Prérequis</title>
-
- <para>
- Les compétences dans sa langue d'un traducteur ne seront pas jugées — cette
- section concerne uniquement les outils logiciels. Théoriquement, seul un
- éditeur de texte est nécessaire. Mais ceci n'est vrai que dans le cas très
- improbable où un traducteur ne souhaiterait pas tester ses traductions des
- messages. Lors de la configuration des sources, il faudra s'assurer d'utiliser l'option
- <option>--enable-nls</option>. Ceci assurera également la présence de la
- bibliothèque <application>libintl</application> et du programme
- <filename>msgfmt</filename> dont tous les utilisateurs finaux ont indéniablement
- besoin. Pour tester son travail, il sera utile de suivre les parties pertinentes
- des instructions d'installation.
- </para>
-
- <para>
- Pour commencer un nouvel effort de traduction ou pour faire un assemblage de
- catalogues de messages (décrit ci-après), il faudra installer respectivement
- les programmes <filename>xgettext</filename> et <filename>msgmerge</filename>
- dans une implémentation compatible GNU. Il est prévu dans le futur que
- <filename>xgettext</filename> ne soit plus nécessaire lorsqu'une distribution
- empaquetée des sources est utilisée (en travaillant à partir du CVS, il
- sera toujours utile). <application>GNU Gettext 0.10.36</application> ou ultérieure est
- actuellement recommandé.
- </para>
-
- <para>
- Toute implémentation locale de gettext devrait être disponible avec sa
- propre documentation. Une partie en est certainement dupliquée dans ce qui suit
- mais des détails complémentaires y sont certainement disponibles.
- </para>
- </sect2>
-
- <sect2>
- <title>Concepts</title>
-
- <para>
- Les couples de messages originaux (anglais) et de leurs (possibles)
- traductions sont conservés dans les <firstterm>catalogues de
- messages</firstterm>, un pour chaque programme (bien que des programmes liés
- puissent partager un catalogue de messages) et pour chaque langue cible. Il
- existe deux formats de fichiers pour les catalogues de messages : le
- premier est le fichier <quote>PO</quote> (pour "Portable Object" ou Objet Portable),
- qui est un
- fichier texte muni d'une syntaxe spéciale et que les traducteurs éditent. Le
- second est un fichier <quote>MO</quote> (pour "Machine Object" ou Objet Machine),
- qui est un
- fichier binaire engendré à partir du fichier PO respectif et qui est utilisé
- lorsque le programme internationalisé est exécuté. Les traducteurs ne
- s'occupent pas des fichiers MO ; en fait, quasiment personne ne s'en occupe.
- </para>
-
- <para>
- L'extension du fichier de catalogue de messages est, sans surprise, soit
- <filename>.po</filename>, soit <filename>.mo</filename>. Le nom de base est
- soit le nom du programme qu'il accompagne soit la langue utilisée dans le
- fichier, suivant la situation. Ceci peut s'avérer être une source de
- confusion. Des exemples sont <filename>psql.po</filename> (fichier PO pour
- psql) ou <filename>fr.mo</filename> (fichier MO en français).
- </para>
-
- <para>
- Le format du fichier PO est illustré ici :
-<programlisting># commentaire
-
-msgid "chaîne originale"
-msgstr "chaîne traduite"
-
-msgid "encore une originale"
-msgstr "encore une de traduite"
-"les chaînes peuvent être sur plusieurs lignes, comme ceci"
-
-...
-</programlisting>
- Les chaînes msgid sont extraites des sources du programme. (Elles n'ont pas
- besoin de l'être mais c'est le moyen le plus commun). Les lignes msgstr sont
- initialement vides puis complétées avec les chaînes traduites. Les chaînes
- peuvent contenir des caractères d'échappement de style C et peuvent être sur
- plusieurs lignes comme le montre l'exemple ci-dessus (la ligne suivante
- doit démarrer au début de la ligne).
- </para>
-
- <para>
- Le caractère # introduit un commentaire. Si une espace fine suit
- immédiatement le caractère #, c'est qu'il s'agit là d'un commentaire maintenu par le
- traducteur. On trouve aussi des commentaires automatiques qui n'ont pas d'espace
- fine suivant immédiatement le caractère #. Ils sont maintenus par les différents outils
- qui opèrent sur les fichiers PO et ont pour but d'aider le traducteur.
-<programlisting>#. commentaire automatique
-#: fichier.c:1023
-#, drapeau, drapeau
-</programlisting>
- Les commentaires du style #. sont extraits du fichier source où le message
- est utilisé. Il est possible que le développeur ait ajouté des informations
- pour le traducteur, telles que l'alignement attendu. Le commentaire #:
- indique l'emplacement exact où le message est utilisé dans le source. Le
- traducteur n'a pas besoin de regarder le source du programme, mais il peut
- le faire s'il subsiste un doute sur l'exactitude d'une traduction. Le commentaire #,
- contient des drapeaux décrivant le message d'une certaine façon. Il existe
- actuellement deux drapeaux : <literal>fuzzy</literal> est positionné si le
- message risque d'être rendu obsolète par des changements dans les
- sources. Le traducteur peut alors vérifier ceci et supprimer ce drapeau.
- Notez que les messages <quote>fuzzy</quote> ne sont pas accessibles à
- l'utilisateur final. L'autre drapeau est <literal>c-format</literal>
- indiquant que le message utilise le format de la fonction C
- <function>printf</function>. Ceci signifie que la traduction devrait aussi
- être de ce format avec le même nombre et le même type de paramètres fictifs. Il
- existe des outils qui vérifient que le message est une chaîne au format
- printf et valident le drapeau c-format en conséquence.
-<!-- which key off the c-format flags -->
- </para>
- </sect2>
-
- <sect2>
- <title>Créer et maintenir des catalogues de messages</title>
-
- <para>
- OK, alors comment faire pour créer un catalogue de messages
- <quote>vide</quote> ? Tout d'abord, se placer dans le répertoire contenant
- le programme dont on souhaite traduire les messages. S'il existe un
- fichier <filename>nls.mk</filename>, alors ce programme est préparé pour la
- traduction.
- </para>
-
- <para>
- S'il y a déjà des fichiers <filename>.po</filename>, alors quelqu'un a
- déjà réalisé des travaux de traduction. Les fichiers sont nommés
- <filename><replaceable>langue</replaceable>.po</filename>, où
- <replaceable>langue</replaceable> est le code de langue sur deux
- caractères (en minuscules) tel que défini par l'<ulink
- url="http://lcweb.loc.gov/standards/iso639-2/englangn.html">
- ISO 639-1, le code du pays composé de deux lettres en minuscule</ulink>,
- c'est-à-dire <filename>fr.po</filename> pour
- le français. S'il existe réellement un besoin pour plus d'une
- traduction par langue, alors les fichiers peuvent être renommés
- <filename><replaceable>langue</replaceable>_<replaceable>region</replaceable>.po</filename>
- où <replaceable>region</replaceable> est le code de langue sur deux
- caractères (en majuscules), tel que défini par l'<ulink
- url="http://www.din.de/gremien/nas/nabd/iso3166ma/codlstp1/en_listp1.html">ISO 3166-1,
- le code du payes sur deux lettres en majuscule</ulink>,
- c'est-à-dire <filename>pt_BR.po</filename> pour le portugais du Brésil. Si vous
- trouvez la langue que vous souhaitez, vous pouvez commencer à travailler
- sur ce fichier.
- </para>
-
- <para>
- Pour commencer une nouvelle traduction, il faudra préalablement exécuter la
- commande :
-<programlisting>gmake init-po
-</programlisting>
- Ceci créera un fichier
- <filename><replaceable>nomprog</replaceable>.pot</filename>.
- (<filename>.pot</filename> pour le distinguer des fichiers PO qui sont
- <quote>en production</quote>. Le <literal>T</literal> signifie
- <quote>template</quote> (NdT : modèle en anglais).
- On copiera ce fichier sous le nom
- <filename><replaceable>langue</replaceable>.po</filename>. On peut alors l'éditer.
- Pour faire savoir qu'une nouvelle langue est disponible, il faut également éditer
- le fichier <filename>nls.mk</filename> et y ajouter le code de la langue (ou de
- la langue et du pays) avec une ligne ressemblant à ceci :
-<programlisting>AVAIL_LANGUAGES := de fr
-</programlisting>
- (d'autres langues peuvent apparaître, bien entendu).
- </para>
-
- <para>
- À mesure que le programme ou la bibliothèque change, des messages peuvent
- être modifiés ou ajoutés par les développeurs. Dans ce cas, il n'est pas nécessaire
- de tout recommencer depuis le début. À la place, on lancera la commande :
-<programlisting>gmake update-po
-</programlisting>
- qui créera un nouveau catalogue de messages vides (le fichier pot avec
- lequel la traduction a été initiée) et le fusionnera avec les fichiers PO existants.
- Si l'algorithme de fusion a une incertitude sur un message particulier,
- il le marquera <quote>fuzzy</quote> comme expliqué ci-dessus. Le nouveau
- fichier PO est sauvegardé avec l'extension <filename>.po.new</filename>.
- </para>
- </sect2>
-
- <sect2>
- <title>Éditer les fichiers PO</title>
-
- <para>
- Les fichiers PO sont éditables avec un éditeur de texte standard. Le traducteur
- doit seulement modifier l'emplacement entre les guillemets après la
- directive msgstr, peut ajouter des commentaires et modifier le drapeau fuzzy
- (NdA : Il existe, ce qui n'est pas surprenant, un mode PO pour Emacs, que je trouve assez
- utile).
- </para>
-
- <para>
- Les fichiers PO n'ont pas besoin d'être entièrement remplis. Le logiciel
- retournera automatiquement à la chaîne originale si une traduction n'est pas
- disponible ou est laissée vide. Soumettre des traductions incomplètes pour les
- inclure dans l'arborescence des sources n'est pas un problème ; cela permet à d'autres
- personnes de récupérer le travail commencé pour le continuer. Néanmoins,
- les traducteurs sont encouragés à donner une haute priorité à la suppression
- des entrées fuzzy après avoir fait une fusion. Les entrées fuzzy ne
- seront pas installées ; elles servent seulement de référence à ce qui
- pourrait être une bonne traduction.
- </para>
-
- <para>
- Certaines choses sont à garder à l'esprit lors de l'édition des
- traductions :
- <itemizedlist>
- <listitem>
- <para>
- S'assurer que si la chaîne originale se termine par un retour chariot, la
- traduction le fasse bien aussi. De même pour les tabulations, etc.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Si la chaîne originale est une chaîne au format <function>printf</function>, la
- traduction doit l'être aussi. La traduction doit également avoir les
- même spécificateurs de format et dans le même ordre. Quelques fois, les
- règles naturelles de la langue rendent cela impossible ou tout au moins
- difficile. Dans ce cas, il est possible de modifier les spécificateurs de format
- de la façon suivante :
-<programlisting>msgstr "Die Datei %2$s hat %1$u Zeichen."
-</programlisting>
- Le premier paramètre fictif sera alors utilisé par le deuxième argument de la
- liste. Le <literal><replaceable>chiffre</replaceable>$</literal> a
- besoin de suivre immédiatement le %, avant tout autre manipulateur de
- format (cette fonctionnalité existe réellement dans la famille des
- fonctions <function>printf</function>, mais elle est peu connue, n'ayant
- que peu d'utilité en dehors de l'internationalisation des messages).
- </para>
- </listitem>
-
- <listitem>
- <para>
- Si la chaîne originale contient une erreur linguistique, on pourra la rapporter
- (ou la corriger soi-même dans le source du programme) et la traduire
- normalement. La chaîne corrigée peut être fusionnée lorsque les
- programmes sources auront été mis à jour. Si la chaîne originale contient
- une erreur factuelle, on la rapportera (ou la corrigera soi-même) mais on
- ne la traduira pas. À la place, on marquera la chaîne avec un commentaire
- dans le fichier PO.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Maintenir le style et le ton de la chaîne originale. En particulier, les
- messages qui ne sont pas des phrases (<literal>cannot
- open file %s</literal>, soit <literal>ne peut pas ouvrir le fichier
- %s</literal>) ne devraient probablement pas commencer avec une lettre
- capitale (si votre langue distingue la casse des lettres) ou finir avec
- un point (si votre langue utilise des marques de ponctuation). Lire <xref
- linkend="error-style-guide"/> peut aider.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Lorsque la signification d'un message n'est pas connue ou s'il est ambigü,
- on pourra demander sa signification sur la liste de diffusion des développeurs.
- Il est possible qu'un anglophone puisse aussi ne pas le comprendre ou le
- trouver ambigü. Il est alors préférable d'améliorer le message.
- </para>
- </listitem>
-
- </itemizedlist>
- </para>
- </sect2>
-
- </sect1>
-
-
- <sect1 id="nls-programmer">
- <title>Pour le développeur</title>
-
- <sect2 id="nls-mechanics">
- <title>Mécaniques</title>
-
- <para>
- Cette section explique comment implémenter le support natif d'une langue dans
- un programme ou dans une bibliothèque qui fait partie de la distribution
- <productname>PostgreSQL</productname>. Actuellement, cela s'applique uniquement aux
- programmes C.
- </para>
-
- <procedure>
- <title>Ajouter le support NLS à un programme</title>
-
- <step>
- <para>
- Le code suivant est inséré dans la séquence initiale du programme :
-<programlisting>#ifdef ENABLE_NLS
-#include <locale.h>
-#endif
-
-...
-
-#ifdef ENABLE_NLS
-setlocale(LC_ALL, "");
-bindtextdomain("<replaceable>nomprog</replaceable>", LOCALEDIR);
-textdomain("<replaceable>nomprog</replaceable>");
-#endif
-</programlisting>
- (<replaceable>nomprog</replaceable> peut être choisi tout à fait librement).
- </para>
- </step>
-
- <step>
- <para>
- Partout où un message candidat à la traduction est trouvé,
- un appel à <function>gettext()</function> doit être inséré.
- Par exemple :
-<programlisting>fprintf(stderr, "panic level %d\n", lvl);
-</programlisting>
- devra être changé avec :
-<programlisting>fprintf(stderr, gettext("panic level %d\n"), lvl);
-</programlisting>
- (<symbol>gettext</symbol> est défini comme une opération nulle si NLS
- n'est pas configuré).
- </para>
-
- <para>
- Cela peut engendrer du fouillis. Un raccourci habituel consiste à
- utiliser :
-<programlisting>#define _(x) gettext(x)
-</programlisting>
- Une autre solution est envisageable si le programme effectue la plupart de ses
- communications via une fonction ou un nombre restreint de fonctions, telle
- <function>ereport()</function> pour le moteur. Le fonctionnement interne de
- cette fonction peut alors être modifiée pour qu'elle appelle
- <function>gettext</function> pour toutes les chaînes en entrée.
- </para>
- </step>
-
- <step>
- <para>
- Un fichier <filename>nls.mk</filename> est ajouté dans le répertoire des
- sources du programme. Ce fichier sera lu comme un makefile. Les affectations
- des variables suivantes doivent être réalisées ici :
-
- <variablelist>
- <varlistentry>
- <term><varname>CATALOG_NAME</varname></term>
-
- <listitem>
- <para>
- Le nom du programme tel que fourni lors de l'appel à
- <function>textdomain()</function>.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>AVAIL_LANGUAGES</varname></term>
-
- <listitem>
- <para>
- Liste des traductions fournies — initialement vide.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>GETTEXT_FILES</varname></term>
-
- <listitem>
- <para>
- Liste des fichiers contenant les chaînes traduisibles, c'est-à-dire
- celles marquées avec <function>gettext</function> ou avec une
- solution alternative. Il se peut que cette liste inclut pratiquement
- tous les fichiers sources du programme. Si cette liste est trop longue,
- le premier <quote>fichier</quote> peut être remplacé par un <literal>+</literal>
- et le deuxième mot représenter un fichier contenant un nom de fichier par
- ligne.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>GETTEXT_TRIGGERS</varname></term>
-
- <listitem>
- <para>
- Les outils qui engendrent des catalogues de messages pour les
- traducteurs ont besoin de connaître les appels de
- fonction contenant des chaînes à traduire. Par défaut, seuls les appels
- à <function>gettext()</function> sont reconnus. Si <function>_</function>
- ou d'autres identifiants sont utilisés, il est nécessaire de les lister ici.
- Si la chaîne traduisible n'est pas le premier argument,
- l'élément a besoin d'être de la forme <literal>func:2</literal> (pour
- le second argument).
- Si vous avez une fonction qui supporte les messages au format pluriel,
- l'élément ressemblera à <literal>func:1,2</literal>
- (identifiant les arguments singulier et pluriel du message).
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
- </para>
- </step>
-
- </procedure>
-
- <para>
- Le système de construction s'occupera automatiquement de construire et
- installer les catalogues de messages.
- </para>
- </sect2>
-
- <sect2 id="nls-guidelines">
- <title>Guide d'écriture des messages</title>
-
- <para>
- Voici quelques lignes de conduite pour l'écriture de messages facilement
- traduisibles.
-
- <itemizedlist>
- <listitem>
- <para>
- Ne pas construire de phrases à l'exécution, telles que :
-<programlisting>printf("Files were %s.\n", flag ? "copied" : "removed");
-</programlisting>
- L'ordre des mots d'une phrase peut être différent dans d'autres langues.
- De plus, même si <function>gettext()</function> est correctement appelé sur chaque fragment,
- il pourrait être difficile de traduire séparément les fragments. Il est
- préférable de dupliquer un peu de code de façon à ce que chaque message à
- traduire forme un tout cohérent. Seuls les nombres, noms de fichiers et
- autres variables d'exécution devraient être insérés au moment de
- l'exécution dans le texte d'un message.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Pour des raisons similaires, ceci ne fonctionnera pas :
-<programlisting>printf("copied %d file%s", n, n!=1 ? "s" : "");
-</programlisting>
- parce que cette forme présume de la façon dont la forme plurielle est obtenue.
- L'idée de résoudre ce cas de la façon suivante :
-<programlisting>if (n==1)
- printf("copied 1 file");
-else
- printf("copied %d files", n):
-</programlisting>
- sera source de déception. Certaines langues ont plus de deux formes avec
- des règles particulières. Il est souvent préférable de concevoir le
- message de façon à éviter le problème, par exemple ainsi&snbp;:
-<programlisting>printf("number of copied files: %d", n);
-</programlisting>
- </para>
-
- <para>
- Si vous voulez vraiment construire un message correctement pluralisé,
- il existe un support pour cela, mais il est un peu étrange. Quand vous
- générez un message d'erreur primaire ou détaillé dans
- <function>ereport()</function>, vous pouvez écrire quelque-chose comme
- ceci :
-<programlisting>
-errmsg_plural("copied %d file",
- "copied %d files",
- n,
- n)
-</programlisting>
- Le premier argument est le chaîne dans le format approprié pour la forme
- au singulier en anglais, le second est le format de chaîne approprié pour
- la forme plurielle en anglais, et le troisième est la valeur entière
- déterminant la forme à utiliser. Des arguments additionnels sont formatés
- suivant la chaîne de formatage comme d'habitude. (Habituellement, la
- valeur de contrôle de la pluralisation sera aussi une des valeurs à
- formater, donc elle sera écrite deux fois.) En anglais, cela n'importe
- que si <replaceable>n</replaceable> est égale à 1 ou est différent de 1,
- mais dans d'autres langues, il pourrait y avoir plusieurs formes de
- pluriel. Le traducteur voit les deux formes anglaises comme un groupe
- et a l'opportunité de fournir des chaînes de substitution supplémentaires,
- la bonne étant sélectionnée suivant la valeur à l'exécution de
- <replaceable>n</replaceable>.
- </para>
-
- <para>
- Si vous avez besoin de pluraliser un message qui ne va pas directement à
- <function>errmsg</function> ou <function>errdetail</function>, vous devez
- utiliser la fonction sous-jacente <function>ngettext</function>. Voir la
- documentation gettext.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Lorsque quelque chose doit être communiqué au traducteur, telle que la façon
- dont un message doit être aligné avec quelque autre sortie, on pourra faire
- précéder l'occurrence de la chaîne d'un commentaire commençant par
- <literal>translator</literal>, par exemple :
-<programlisting>/* translator: This message is not what it seems to be. */
-</programlisting>
- Ces commentaires sont copiés dans les catalogues de messages de façon à
- ce que les traducteurs les voient.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- </sect2>
- </sect1>
-
-</chapter>
Copied: traduc/tags/tv840rc1/nls.xml (from rev 1350, traduc/trunk/postgresql/nls.xml)
===================================================================
--- traduc/tags/tv840rc1/nls.xml (rev 0)
+++ traduc/tags/tv840rc1/nls.xml 2009-06-23 16:25:46 UTC (rev 1351)
@@ -0,0 +1,533 @@
+<?xml version="1.0" encoding="ISO-8859-15"?>
+<!-- Dernière modification
+ le $Date$
+ par $Author$
+ révision $Revision$ -->
+
+<chapter id="nls">
+ <chapterinfo>
+ <author>
+ <firstname>Peter</firstname>
+ <surname>Eisentraut</surname>
+ </author>
+ </chapterinfo>
+
+ <title>Support natif des langues</title>
+
+ <sect1 id="nls-translator">
+ <title>Pour le traducteur</title>
+
+ <para>
+ Les programmes <productname>PostgreSQL</productname> (serveur et client) peuvent afficher
+ leur message dans la langue préférée de l'utilisateur — si les messages ont été traduits.
+ Créer et maintenir les ensembles de messages traduits nécessite l'aide de
+ personnes parlant leur propre langue et souhaitant contribuer à
+ <productname>PostgreSQL</productname>. Il n'est nul besoin d'être un développeur
+ pour cela. Cette section explique comment apporter son aide.
+ </para>
+
+ <sect2>
+ <title>Prérequis</title>
+
+ <para>
+ Les compétences dans sa langue d'un traducteur ne seront pas jugées — cette
+ section concerne uniquement les outils logiciels. Théoriquement, seul un
+ éditeur de texte est nécessaire. Mais ceci n'est vrai que dans le cas très
+ improbable où un traducteur ne souhaiterait pas tester ses traductions des
+ messages. Lors de la configuration des sources, il faudra s'assurer d'utiliser l'option
+ <option>--enable-nls</option>. Ceci assurera également la présence de la
+ bibliothèque <application>libintl</application> et du programme
+ <filename>msgfmt</filename> dont tous les utilisateurs finaux ont indéniablement
+ besoin. Pour tester son travail, il sera utile de suivre les parties pertinentes
+ des instructions d'installation.
+ </para>
+
+ <para>
+ Pour commencer un nouvel effort de traduction ou pour faire un assemblage de
+ catalogues de messages (décrit ci-après), il faudra installer respectivement
+ les programmes <filename>xgettext</filename> et <filename>msgmerge</filename>
+ dans une implémentation compatible GNU. Il est prévu dans le futur que
+ <filename>xgettext</filename> ne soit plus nécessaire lorsqu'une distribution
+ empaquetée des sources est utilisée (en travaillant à partir du CVS, il
+ sera toujours utile). <application>GNU Gettext 0.10.36</application> ou ultérieure est
+ actuellement recommandé.
+ </para>
+
+ <para>
+ Toute implémentation locale de gettext devrait être disponible avec sa
+ propre documentation. Une partie en est certainement dupliquée dans ce qui suit
+ mais des détails complémentaires y sont certainement disponibles.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Concepts</title>
+
+ <para>
+ Les couples de messages originaux (anglais) et de leurs (possibles)
+ traductions sont conservés dans les <firstterm>catalogues de
+ messages</firstterm>, un pour chaque programme (bien que des programmes liés
+ puissent partager un catalogue de messages) et pour chaque langue cible. Il
+ existe deux formats de fichiers pour les catalogues de messages : le
+ premier est le fichier <quote>PO</quote> (pour "Portable Object" ou Objet Portable),
+ qui est un
+ fichier texte muni d'une syntaxe spéciale et que les traducteurs éditent. Le
+ second est un fichier <quote>MO</quote> (pour "Machine Object" ou Objet Machine),
+ qui est un
+ fichier binaire engendré à partir du fichier PO respectif et qui est utilisé
+ lorsque le programme internationalisé est exécuté. Les traducteurs ne
+ s'occupent pas des fichiers MO ; en fait, quasiment personne ne s'en occupe.
+ </para>
+
+ <para>
+ L'extension du fichier de catalogue de messages est, sans surprise, soit
+ <filename>.po</filename>, soit <filename>.mo</filename>. Le nom de base est
+ soit le nom du programme qu'il accompagne soit la langue utilisée dans le
+ fichier, suivant la situation. Ceci peut s'avérer être une source de
+ confusion. Des exemples sont <filename>psql.po</filename> (fichier PO pour
+ psql) ou <filename>fr.mo</filename> (fichier MO en français).
+ </para>
+
+ <para>
+ Le format du fichier PO est illustré ici :
+<programlisting># commentaire
+
+msgid "chaîne originale"
+msgstr "chaîne traduite"
+
+msgid "encore une originale"
+msgstr "encore une de traduite"
+"les chaînes peuvent être sur plusieurs lignes, comme ceci"
+
+...
+</programlisting>
+ Les chaînes msgid sont extraites des sources du programme. (Elles n'ont pas
+ besoin de l'être mais c'est le moyen le plus commun). Les lignes msgstr sont
+ initialement vides puis complétées avec les chaînes traduites. Les chaînes
+ peuvent contenir des caractères d'échappement de style C et peuvent être sur
+ plusieurs lignes comme le montre l'exemple ci-dessus (la ligne suivante
+ doit démarrer au début de la ligne).
+ </para>
+
+ <para>
+ Le caractère # introduit un commentaire. Si une espace fine suit
+ immédiatement le caractère #, c'est qu'il s'agit là d'un commentaire maintenu par le
+ traducteur. On trouve aussi des commentaires automatiques qui n'ont pas d'espace
+ fine suivant immédiatement le caractère #. Ils sont maintenus par les différents outils
+ qui opèrent sur les fichiers PO et ont pour but d'aider le traducteur.
+<programlisting>#. commentaire automatique
+#: fichier.c:1023
+#, drapeau, drapeau
+</programlisting>
+ Les commentaires du style #. sont extraits du fichier source où le message
+ est utilisé. Il est possible que le développeur ait ajouté des informations
+ pour le traducteur, telles que l'alignement attendu. Le commentaire #:
+ indique l'emplacement exact où le message est utilisé dans le source. Le
+ traducteur n'a pas besoin de regarder le source du programme, mais il peut
+ le faire s'il subsiste un doute sur l'exactitude d'une traduction. Le commentaire #,
+ contient des drapeaux décrivant le message d'une certaine façon. Il existe
+ actuellement deux drapeaux : <literal>fuzzy</literal> est positionné si le
+ message risque d'être rendu obsolète par des changements dans les
+ sources. Le traducteur peut alors vérifier ceci et supprimer ce drapeau.
+ Notez que les messages <quote>fuzzy</quote> ne sont pas accessibles à
+ l'utilisateur final. L'autre drapeau est <literal>c-format</literal>
+ indiquant que le message utilise le format de la fonction C
+ <function>printf</function>. Ceci signifie que la traduction devrait aussi
+ être de ce format avec le même nombre et le même type de paramètres fictifs. Il
+ existe des outils qui vérifient que le message est une chaîne au format
+ printf et valident le drapeau c-format en conséquence.
+<!-- which key off the c-format flags -->
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Créer et maintenir des catalogues de messages</title>
+
+ <para>
+ OK, alors comment faire pour créer un catalogue de messages
+ <quote>vide</quote> ? Tout d'abord, se placer dans le répertoire contenant
+ le programme dont on souhaite traduire les messages. S'il existe un
+ fichier <filename>nls.mk</filename>, alors ce programme est préparé pour la
+ traduction.
+ </para>
+
+ <para>
+ S'il y a déjà des fichiers <filename>.po</filename>, alors quelqu'un a
+ déjà réalisé des travaux de traduction. Les fichiers sont nommés
+ <filename><replaceable>langue</replaceable>.po</filename>, où
+ <replaceable>langue</replaceable> est le code de langue sur deux
+ caractères (en minuscules) tel que défini par l'<ulink
+ url="http://lcweb.loc.gov/standards/iso639-2/englangn.html">
+ ISO 639-1, le code du pays composé de deux lettres en minuscule</ulink>,
+ c'est-à-dire <filename>fr.po</filename> pour
+ le français. S'il existe réellement un besoin pour plus d'une
+ traduction par langue, alors les fichiers peuvent être renommés
+ <filename><replaceable>langue</replaceable>_<replaceable>region</replaceable>.po</filename>
+ où <replaceable>region</replaceable> est le code de langue sur deux
+ caractères (en majuscules), tel que défini par l'<ulink
+ url="http://www.din.de/gremien/nas/nabd/iso3166ma/codlstp1/en_listp1.html">ISO 3166-1,
+ le code du payes sur deux lettres en majuscule</ulink>,
+ c'est-à-dire <filename>pt_BR.po</filename> pour le portugais du Brésil. Si vous
+ trouvez la langue que vous souhaitez, vous pouvez commencer à travailler
+ sur ce fichier.
+ </para>
+
+ <para>
+ Pour commencer une nouvelle traduction, il faudra préalablement exécuter la
+ commande :
+<programlisting>gmake init-po
+</programlisting>
+ Ceci créera un fichier
+ <filename><replaceable>nomprog</replaceable>.pot</filename>.
+ (<filename>.pot</filename> pour le distinguer des fichiers PO qui sont
+ <quote>en production</quote>. Le <literal>T</literal> signifie
+ <quote>template</quote> (NdT : modèle en anglais).
+ On copiera ce fichier sous le nom
+ <filename><replaceable>langue</replaceable>.po</filename>. On peut alors l'éditer.
+ Pour faire savoir qu'une nouvelle langue est disponible, il faut également éditer
+ le fichier <filename>nls.mk</filename> et y ajouter le code de la langue (ou de
+ la langue et du pays) avec une ligne ressemblant à ceci :
+<programlisting>AVAIL_LANGUAGES := de fr
+</programlisting>
+ (d'autres langues peuvent apparaître, bien entendu).
+ </para>
+
+ <para>
+ À mesure que le programme ou la bibliothèque change, des messages peuvent
+ être modifiés ou ajoutés par les développeurs. Dans ce cas, il n'est pas nécessaire
+ de tout recommencer depuis le début. À la place, on lancera la commande :
+<programlisting>gmake update-po
+</programlisting>
+ qui créera un nouveau catalogue de messages vides (le fichier pot avec
+ lequel la traduction a été initiée) et le fusionnera avec les fichiers PO existants.
+ Si l'algorithme de fusion a une incertitude sur un message particulier,
+ il le marquera <quote>fuzzy</quote> comme expliqué ci-dessus. Le nouveau
+ fichier PO est sauvegardé avec l'extension <filename>.po.new</filename>.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Éditer les fichiers PO</title>
+
+ <para>
+ Les fichiers PO sont éditables avec un éditeur de texte standard. Le traducteur
+ doit seulement modifier l'emplacement entre les guillemets après la
+ directive msgstr, peut ajouter des commentaires et modifier le drapeau fuzzy
+ (NdA : Il existe, ce qui n'est pas surprenant, un mode PO pour Emacs, que je trouve assez
+ utile).
+ </para>
+
+ <para>
+ Les fichiers PO n'ont pas besoin d'être entièrement remplis. Le logiciel
+ retournera automatiquement à la chaîne originale si une traduction n'est pas
+ disponible ou est laissée vide. Soumettre des traductions incomplètes pour les
+ inclure dans l'arborescence des sources n'est pas un problème ; cela permet à d'autres
+ personnes de récupérer le travail commencé pour le continuer. Néanmoins,
+ les traducteurs sont encouragés à donner une haute priorité à la suppression
+ des entrées fuzzy après avoir fait une fusion. Les entrées fuzzy ne
+ seront pas installées ; elles servent seulement de référence à ce qui
+ pourrait être une bonne traduction.
+ </para>
+
+ <para>
+ Certaines choses sont à garder à l'esprit lors de l'édition des
+ traductions :
+ <itemizedlist>
+ <listitem>
+ <para>
+ S'assurer que si la chaîne originale se termine par un retour chariot, la
+ traduction le fasse bien aussi. De même pour les tabulations, etc.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Si la chaîne originale est une chaîne au format <function>printf</function>, la
+ traduction doit l'être aussi. La traduction doit également avoir les
+ même spécificateurs de format et dans le même ordre. Quelques fois, les
+ règles naturelles de la langue rendent cela impossible ou tout au moins
+ difficile. Dans ce cas, il est possible de modifier les spécificateurs de format
+ de la façon suivante :
+<programlisting>msgstr "Die Datei %2$s hat %1$u Zeichen."
+</programlisting>
+ Le premier paramètre fictif sera alors utilisé par le deuxième argument de la
+ liste. Le <literal><replaceable>chiffre</replaceable>$</literal> a
+ besoin de suivre immédiatement le %, avant tout autre manipulateur de
+ format (cette fonctionnalité existe réellement dans la famille des
+ fonctions <function>printf</function>, mais elle est peu connue, n'ayant
+ que peu d'utilité en dehors de l'internationalisation des messages).
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Si la chaîne originale contient une erreur linguistique, on pourra la rapporter
+ (ou la corriger soi-même dans le source du programme) et la traduire
+ normalement. La chaîne corrigée peut être fusionnée lorsque les
+ programmes sources auront été mis à jour. Si la chaîne originale contient
+ une erreur factuelle, on la rapportera (ou la corrigera soi-même) mais on
+ ne la traduira pas. À la place, on marquera la chaîne avec un commentaire
+ dans le fichier PO.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Maintenir le style et le ton de la chaîne originale. En particulier, les
+ messages qui ne sont pas des phrases (<literal>cannot
+ open file %s</literal>, soit <literal>ne peut pas ouvrir le fichier
+ %s</literal>) ne devraient probablement pas commencer avec une lettre
+ capitale (si votre langue distingue la casse des lettres) ou finir avec
+ un point (si votre langue utilise des marques de ponctuation). Lire <xref
+ linkend="error-style-guide"/> peut aider.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Lorsque la signification d'un message n'est pas connue ou s'il est ambigü,
+ on pourra demander sa signification sur la liste de diffusion des développeurs.
+ Il est possible qu'un anglophone puisse aussi ne pas le comprendre ou le
+ trouver ambigü. Il est alors préférable d'améliorer le message.
+ </para>
+ </listitem>
+
+ </itemizedlist>
+ </para>
+ </sect2>
+
+ </sect1>
+
+
+ <sect1 id="nls-programmer">
+ <title>Pour le développeur</title>
+
+ <sect2 id="nls-mechanics">
+ <title>Mécaniques</title>
+
+ <para>
+ Cette section explique comment implémenter le support natif d'une langue dans
+ un programme ou dans une bibliothèque qui fait partie de la distribution
+ <productname>PostgreSQL</productname>. Actuellement, cela s'applique uniquement aux
+ programmes C.
+ </para>
+
+ <procedure>
+ <title>Ajouter le support NLS à un programme</title>
+
+ <step>
+ <para>
+ Le code suivant est inséré dans la séquence initiale du programme :
+<programlisting>#ifdef ENABLE_NLS
+#include <locale.h>
+#endif
+
+...
+
+#ifdef ENABLE_NLS
+setlocale(LC_ALL, "");
+bindtextdomain("<replaceable>nomprog</replaceable>", LOCALEDIR);
+textdomain("<replaceable>nomprog</replaceable>");
+#endif
+</programlisting>
+ (<replaceable>nomprog</replaceable> peut être choisi tout à fait librement).
+ </para>
+ </step>
+
+ <step>
+ <para>
+ Partout où un message candidat à la traduction est trouvé,
+ un appel à <function>gettext()</function> doit être inséré.
+ Par exemple :
+<programlisting>fprintf(stderr, "panic level %d\n", lvl);
+</programlisting>
+ devra être changé avec :
+<programlisting>fprintf(stderr, gettext("panic level %d\n"), lvl);
+</programlisting>
+ (<symbol>gettext</symbol> est défini comme une opération nulle si NLS
+ n'est pas configuré).
+ </para>
+
+ <para>
+ Cela peut engendrer du fouillis. Un raccourci habituel consiste à
+ utiliser :
+<programlisting>#define _(x) gettext(x)
+</programlisting>
+ Une autre solution est envisageable si le programme effectue la plupart de ses
+ communications via une fonction ou un nombre restreint de fonctions, telle
+ <function>ereport()</function> pour le moteur. Le fonctionnement interne de
+ cette fonction peut alors être modifiée pour qu'elle appelle
+ <function>gettext</function> pour toutes les chaînes en entrée.
+ </para>
+ </step>
+
+ <step>
+ <para>
+ Un fichier <filename>nls.mk</filename> est ajouté dans le répertoire des
+ sources du programme. Ce fichier sera lu comme un makefile. Les affectations
+ des variables suivantes doivent être réalisées ici :
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>CATALOG_NAME</varname></term>
+
+ <listitem>
+ <para>
+ Le nom du programme tel que fourni lors de l'appel à
+ <function>textdomain()</function>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>AVAIL_LANGUAGES</varname></term>
+
+ <listitem>
+ <para>
+ Liste des traductions fournies — initialement vide.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>GETTEXT_FILES</varname></term>
+
+ <listitem>
+ <para>
+ Liste des fichiers contenant les chaînes traduisibles, c'est-à-dire
+ celles marquées avec <function>gettext</function> ou avec une
+ solution alternative. Il se peut que cette liste inclut pratiquement
+ tous les fichiers sources du programme. Si cette liste est trop longue,
+ le premier <quote>fichier</quote> peut être remplacé par un <literal>+</literal>
+ et le deuxième mot représenter un fichier contenant un nom de fichier par
+ ligne.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>GETTEXT_TRIGGERS</varname></term>
+
+ <listitem>
+ <para>
+ Les outils qui engendrent des catalogues de messages pour les
+ traducteurs ont besoin de connaître les appels de
+ fonction contenant des chaînes à traduire. Par défaut, seuls les appels
+ à <function>gettext()</function> sont reconnus. Si <function>_</function>
+ ou d'autres identifiants sont utilisés, il est nécessaire de les lister ici.
+ Si la chaîne traduisible n'est pas le premier argument,
+ l'élément a besoin d'être de la forme <literal>func:2</literal> (pour
+ le second argument).
+ Si vous avez une fonction qui supporte les messages au format pluriel,
+ l'élément ressemblera à <literal>func:1,2</literal>
+ (identifiant les arguments singulier et pluriel du message).
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </step>
+
+ </procedure>
+
+ <para>
+ Le système de construction s'occupera automatiquement de construire et
+ installer les catalogues de messages.
+ </para>
+ </sect2>
+
+ <sect2 id="nls-guidelines">
+ <title>Guide d'écriture des messages</title>
+
+ <para>
+ Voici quelques lignes de conduite pour l'écriture de messages facilement
+ traduisibles.
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Ne pas construire de phrases à l'exécution, telles que :
+<programlisting>printf("Files were %s.\n", flag ? "copied" : "removed");
+</programlisting>
+ L'ordre des mots d'une phrase peut être différent dans d'autres langues.
+ De plus, même si <function>gettext()</function> est correctement appelé sur chaque fragment,
+ il pourrait être difficile de traduire séparément les fragments. Il est
+ préférable de dupliquer un peu de code de façon à ce que chaque message à
+ traduire forme un tout cohérent. Seuls les nombres, noms de fichiers et
+ autres variables d'exécution devraient être insérés au moment de
+ l'exécution dans le texte d'un message.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Pour des raisons similaires, ceci ne fonctionnera pas :
+<programlisting>printf("copied %d file%s", n, n!=1 ? "s" : "");
+</programlisting>
+ parce que cette forme présume de la façon dont la forme plurielle est obtenue.
+ L'idée de résoudre ce cas de la façon suivante :
+<programlisting>if (n==1)
+ printf("copied 1 file");
+else
+ printf("copied %d files", n):
+</programlisting>
+ sera source de déception. Certaines langues ont plus de deux formes avec
+ des règles particulières. Il est souvent préférable de concevoir le
+ message de façon à éviter le problème, par exemple ainsi :
+<programlisting>printf("number of copied files: %d", n);
+</programlisting>
+ </para>
+
+ <para>
+ Si vous voulez vraiment construire un message correctement pluralisé,
+ il existe un support pour cela, mais il est un peu étrange. Quand vous
+ générez un message d'erreur primaire ou détaillé dans
+ <function>ereport()</function>, vous pouvez écrire quelque-chose comme
+ ceci :
+<programlisting>
+errmsg_plural("copied %d file",
+ "copied %d files",
+ n,
+ n)
+</programlisting>
+ Le premier argument est le chaîne dans le format approprié pour la forme
+ au singulier en anglais, le second est le format de chaîne approprié pour
+ la forme plurielle en anglais, et le troisième est la valeur entière
+ déterminant la forme à utiliser. Des arguments additionnels sont formatés
+ suivant la chaîne de formatage comme d'habitude. (Habituellement, la
+ valeur de contrôle de la pluralisation sera aussi une des valeurs à
+ formater, donc elle sera écrite deux fois.) En anglais, cela n'importe
+ que si <replaceable>n</replaceable> est égale à 1 ou est différent de 1,
+ mais dans d'autres langues, il pourrait y avoir plusieurs formes de
+ pluriel. Le traducteur voit les deux formes anglaises comme un groupe
+ et a l'opportunité de fournir des chaînes de substitution supplémentaires,
+ la bonne étant sélectionnée suivant la valeur à l'exécution de
+ <replaceable>n</replaceable>.
+ </para>
+
+ <para>
+ Si vous avez besoin de pluraliser un message qui ne va pas directement à
+ <function>errmsg</function> ou <function>errdetail</function>, vous devez
+ utiliser la fonction sous-jacente <function>ngettext</function>. Voir la
+ documentation gettext.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Lorsque quelque chose doit être communiqué au traducteur, telle que la façon
+ dont un message doit être aligné avec quelque autre sortie, on pourra faire
+ précéder l'occurrence de la chaîne d'un commentaire commençant par
+ <literal>translator</literal>, par exemple :
+<programlisting>/* translator: This message is not what it seems to be. */
+</programlisting>
+ Ces commentaires sont copiés dans les catalogues de messages de façon à
+ ce que les traducteurs les voient.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+ </sect1>
+
+</chapter>
Deleted: traduc/tags/tv840rc1/xfunc.xml
===================================================================
--- traduc/trunk/postgresql/xfunc.xml 2009-06-22 07:28:54 UTC (rev 1349)
+++ traduc/tags/tv840rc1/xfunc.xml 2009-06-23 16:25:46 UTC (rev 1351)
@@ -1,3268 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-15"?>
-<!-- Dernière modification
- le $Date$
- par $Author$
- révision $Revision$ -->
-
- <sect1 id="xfunc">
- <title>Fonctions utilisateur</title>
-
- <indexterm zone="xfunc">
- <primary>fonction</primary>
- <secondary>utilisateur</secondary>
- </indexterm>
-
- <para>
- <productname>PostgreSQL</productname> propose quatre types de
- fonctions :
-
- <itemizedlist>
- <listitem>
- <para>
- fonctions en langage de requête (fonctions écrites en <acronym>SQL</acronym>,
- <xref linkend="xfunc-sql"/>)
- </para>
- </listitem>
- <listitem>
- <para>
- fonctions en langage procédural (fonctions écrites, par exemple, en
- <application>PL/pgSQL</application> ou <application>PL/Tcl</application>,
- <xref linkend="xfunc-pl"/>)
- </para>
- </listitem>
- <listitem>
- <para>
- fonctions internes (<xref linkend="xfunc-internal"/>)
- </para>
- </listitem>
- <listitem>
- <para>
- fonctions en langage C (<xref linkend="xfunc-c"/>)
- </para>
- </listitem>
- </itemizedlist>
- </para>
-
- <para>
- Chaque type de fonction peut accepter comme arguments (paramètres) des types
- de base, des types composites ou une combinaison de ceux-ci. De plus, chaque
- sorte de fonction peut renvoyer un type de base ou un type composite. Les
- fonctions pourraient aussi être définies pour renvoyer des ensembles de valeurs
- de base ou de valeurs composites.
- </para>
-
- <para>
- De nombreuses sortes de fonctions peuvent accepter ou renvoyer certains
- pseudo-types (comme les types polymorphes) mais avec des fonctionnalités
- variées.
- Consultez la description de chaque type de fonction pour plus de détails.
- </para>
-
- <para>
- Il est plus facile de définir des fonctions <acronym>SQL</acronym> aussi
- allons-nous commencer par celles-ci. La plupart des concepts présentés pour
- les fonctions <acronym>SQL</acronym> seront aussi gérés par les autres types
- de fonctions.
- </para>
-
- <para>
- Lors de la lecture de ce chapitre, il peut être utile de consulter la page
- de référence de la commande <xref linkend="sql-createfunction"
- endterm="sql-createfunction-title"/> pour mieux
- comprendre les exemples. Quelques exemples extraits de ce chapitre peuvent
- être trouvés dans les fichiers <filename>funcs.sql</filename> et
- <filename>funcs.c</filename> du répertoire du tutoriel de la distribution
- source de <productname>PostgreSQL</productname>.
- </para>
-
- </sect1>
-
- <sect1 id="xfunc-sql">
- <title>Fonctions en langage de requêtes (<acronym>SQL</acronym>)</title>
-
- <indexterm zone="xfunc-sql">
- <primary>fonction</primary>
- <secondary>définie par l'utilisateur</secondary>
- <tertiary>en SQL</tertiary>
- </indexterm>
-
- <para>
- Les fonctions SQL exécutent une liste arbitraire d'instructions SQL et
- renvoient le résultat de la dernière requête de cette liste. Dans le cas
- d'un résultat simple (pas d'ensemble), la première ligne du résultat de la
- dernière requête sera renvoyée (gardez à l'esprit que <quote>la première
- ligne</quote> d'un résultat multiligne n'est pas bien définie à moins
- d'utiliser <literal>ORDER BY</literal>). Si la dernière requête de la liste ne
- renvoie aucune ligne, la valeur NULL est renvoyée.
- </para>
-
- <para>
- Une fonction SQL peut être déclarée de façon à renvoyer un ensemble (set)
- en spécifiant le type renvoyé par la fonction comme <literal>SETOF
- <replaceable>un_type</replaceable></literal>, ou de façon équivalente en
- la déclarant comme <literal>RETURNS
- TABLE(<replaceable>colonnes</replaceable>)</literal>. Dans ce
- cas, toutes les lignes de la dernière requête sont renvoyées. Des détails
- supplémentaires sont donnés plus loin dans ce chapitre.
- </para>
-
- <para>
- Le corps d'une fonction SQL doit être constitué d'une liste d'une ou
- de plusieurs instructions SQL séparées par des points-virgule. Un
- point-virgule après la dernière instruction est optionnel. Sauf si la
- fonction déclare renvoyer <type>void</type>, la dernière instruction doit
- être un <command>SELECT</command> ou un <command>INSERT</command>,
- <command>UPDATE</command> ou un <command>DELETE</command> qui a une clause
- <literal>RETURNING</literal>.
- </para>
-
- <para>
- Toute collection de commandes dans le langage <acronym>SQL</acronym> peut
- être assemblée et définie comme une fonction. En plus des requêtes
- <command>SELECT</command>, les commandes peuvent inclure des requêtes de
- modification des données (<command>INSERT</command>,
- <command>UPDATE</command> et <command>DELETE</command>) ainsi que
- d'autres commandes SQL (la seule exception est que vous ne pouvez pas
- placer de commandes <command>BEGIN</command>, <command>COMMIT</command>,
- <command>ROLLBACK</command> ou <command>SAVEPOINT</command> dans une fonction
- <acronym>SQL</acronym>). Néanmoins, la commande finale doit être un
- <command>SELECT</command> ou doit avoir une clause <literal>RETURNING</literal>
- qui renvoie ce qui a été spécifié comme type
- de retour de la fonction. Autrement, si vous voulez définir une fonction
- SQL qui réalise des actions mais n'a pas de valeur utile à renvoyer,
- vous pouvez la définir comme renvoyant <type>void</type>. Par exemple, cette fonction supprime les
- lignes avec des salaires négatifs depuis la table <literal>emp</literal> :
-
-<screen>CREATE FUNCTION nettoie_emp() RETURNS void AS '
- DELETE FROM emp WHERE salaire < 0;
-' LANGUAGE SQL;
-
-SELECT nettoie_emp();
-
- nettoie_emp
- -----------
-
- (1 row)
-</screen>
- </para>
-
- <para>
- La syntaxe de la commande <command>CREATE FUNCTION</command> requiert que
- le corps de la fonction soit écrit comme une constante de type chaîne.
- Il est habituellement plus agréable d'utiliser les guillemets dollar
- (voir la <xref linkend="sql-syntax-dollar-quoting"/>) pour cette constante.
- Si vous choisissez d'utiliser la syntaxe habituelle avec des guillemets
- simples, vous devez doubler les marques de guillemet simple
- (<literal>'</literal>) et les antislashs (<literal>\</literal>), en supposant
- que vous utilisez la syntaxe d'échappement de chaînes, utilisés dans le corps
- de la fonction (voir la <xref linkend="sql-syntax-strings"/>).
- </para>
-
- <para>
- Les arguments de la fonction SQL sont référencés dans le corps de la
- fonction en utilisant la syntaxe suivante.
- <literal>$<replaceable>n</replaceable></literal>:<literal>$1</literal> se réfère au premier argument,
- <literal>$2</literal> au second et ainsi de suite. Si un argument est de type
- composite, on utilisera la notation par point, par exemple
- <literal>$1.name</literal>, pour accéder aux attributs de l'argument.
- Les arguments peuvent seulement être utilisés comme valeurs des données, pas
- comme des identifieurs. Du coup, par exemple, ceci est correct :
-<programlisting>INSERT INTO matable VALUES ($1);
-</programlisting>
- mais ceci ne fonctionnera pas :
-<programlisting>INSERT INTO $1 VALUES (42);
-</programlisting>
- </para>
-
- <sect2 id="xfunc-sql-base-functions">
- <title>Fonctions <acronym>SQL</acronym> sur les types de base</title>
-
- <para>
- La fonction <acronym>SQL</acronym> la plus simple possible n'a pas
- d'argument et retourne un type de base tel que <type>integer</type> :
-<screen>CREATE FUNCTION un() RETURNS integer AS $$
- SELECT 1 AS resultat;
-$$ LANGUAGE SQL;
-
--- Autre syntaxe pour les chaînes littérales :
-CREATE FUNCTION un() RETURNS integer AS '
- SELECT 1 AS resultat;
-' LANGUAGE SQL;
-
-SELECT un();
-
- un
-----
- 1
-</screen>
- </para>
-
- <para>
- Notez que nous avons défini un alias de colonne avec le nom
- <literal>resultat</literal> dans le corps de la fonction pour se référer au
- résultat de la fonction mais cet alias n'est pas visible hors de la
- fonction. En effet, le résultat est nommé <literal>un</literal> au lieu de
- <literal>resultat</literal>.
- </para>
-
- <para>
- Il est presque aussi facile de définir des fonctions SQL acceptant des types
- de base comme arguments. Dans l'exemple suivant, remarquez comment nous
- faisons référence aux arguments dans le corps de la fonction avec
- <literal>$1</literal> et <literal>$2</literal>.
-
-<screen>CREATE FUNCTION ajoute(integer, integer) RETURNS integer AS $$
- SELECT $1 + $2;
-$$ LANGUAGE SQL;
-
-SELECT ajoute(1, 2) AS reponse;
-
- reponse
----------
- 3
-</screen>
- </para>
-
- <para>
- Voici une fonction plus utile, qui pourrait être utilisée pour débiter un
- compte bancaire :
-
-<programlisting>CREATE FUNCTION tf1 (integer, numeric) RETURNS integer AS $$
- UPDATE banque
- SET balance = balance - $2
- WHERE no_compte = $1;
- SELECT 1;
-$$ LANGUAGE SQL;
-</programlisting>
-
- Un utilisateur pourrait exécuter cette fonction pour débiter le compte 17 de
- 100 000 euros ainsi :
-<programlisting>SELECT tf1(17, 100.000);</programlisting>
- </para>
-
- <para>
- Dans la pratique, on préférera vraisemblablement un résultat plus utile que
- la constante 1. Une définition plus probable est :
-
-<programlisting>CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$
- UPDATE banque
- SET balance = balance - $2
- WHERE no_compte = $1;
- SELECT balance FROM banque WHERE no_compte = $1;
-$$ LANGUAGE SQL;
-</programlisting>
-
- qui ajuste le solde et renvoie sa nouvelle valeur.
- La même chose peut se faire en une commande en utilisant la clause
- <literal>RETURNING</literal> :
-
-<programlisting>
-CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$
- UPDATE banque
- SET balance = balance - $2
- WHERE no_compte = $1
- RETURNING balance;
-$$ LANGUAGE SQL;
-</programlisting>
- </para>
-
- </sect2>
-
- <sect2>
- <title>Fonctions <acronym>SQL</acronym> sur les types composites</title>
-
- <para>
- Quand nous écrivons une fonction avec des arguments de type composite,
- nous devons non seulement spécifier l'argument utilisé (comme nous
- l'avons fait précédemment avec <literal>$1</literal> et <literal>$2</literal>),
- mais aussi spécifier l'attribut désiré de cet argument (champ). Par
- exemple, supposons que
- <type>emp</type> soit le nom d'une table contenant des données sur les
- employés et donc également le nom du type composite correspondant à chaque
- ligne de la table. Voici une fonction <function>double_salaire</function>
- qui calcule ce que serait le salaire de quelqu'un s'il était doublé :
-
-<screen>CREATE TABLE emp (
- nom text,
- salaire numeric,
- age integer,
- cubicle point
-);
-
-CREATE FUNCTION double_salaire(emp) RETURNS numeric AS $$
- SELECT $1.salaire * 2 AS salaire;
-$$ LANGUAGE SQL;
-
-SELECT nom, double_salaire(emp.*) AS reve
- FROM emp
- WHERE emp.cubicle ~= point '(2,1)';
-
- name | reve
-------+-------
- Bill | 8400
-</screen>
- </para>
-
- <para>
- Notez l'utilisation de la syntaxe <literal>$1.salaire</literal> pour
- sélectionner un champ dans la valeur de la ligne argument. Notez également
- comment la commande <command>SELECT</command> utilise <literal>*</literal> pour
- sélectionner la ligne courante entière de la table comme une valeur composite
- (<literal>emp</literal>). La ligne de la table peut aussi être référencée en
- utilisant seulement le nom de la table ainsi :
-<screen>SELECT nom, double_salaire(emp) AS reve
- FROM emp
- WHERE emp.cubicle ~= point '(2,1)';
-</screen>
- mais cette utilisation est obsolète car elle est facilement obscure.
- </para>
-
- <para>
- Quelque fois, il est pratique de construire une valeur d'argument
- composite en direct. Ceci peut se faire avec la construction
- <literal>ROW</literal>. Par exemple, nous pouvons ajuster les données passées
- à la fonction :
-<screen>SELECT nom, double_salaire(ROW(nom, salaire*1.1, age, cubicle)) AS reve
- FROM emp;
-</screen>
- </para>
-
- <para>
- Il est aussi possible de construire une fonction qui renvoie un type
- composite. Voici un exemple de fonction renvoyant une seule ligne de type
- <type>emp</type> :
-
-<programlisting>CREATE FUNCTION nouvel_emp() RETURNS emp AS $$
- SELECT text 'Aucun' AS nom,
- 1000.0 AS salaire,
- 25 AS age,
- point '(2,2)' AS cubicle;
-$$ LANGUAGE SQL;
-</programlisting>
-
- Dans cet exemple, nous avons spécifié chacun des attributs avec une valeur
- constante, mais un quelconque calcul aurait pu être substitué à ces
- valeurs.
- </para>
-
- <para>
- Notez deux aspects importants à propos de la définition de fonction :
-
- <itemizedlist>
- <listitem>
- <para>
- L'ordre de la liste du SELECT doit être exactement le même que celui
- dans lequel les colonnes apparaissent dans la table associée au type
- composite (donner des noms aux colonnes dans le corps de la fonction,
- comme nous l'avons
- fait dans l'exemple, n'a aucune interaction avec le système).
- </para>
- </listitem>
- <listitem>
- <para>
- Vous devez transtyper les expressions pour concorder avec la définition
- du type composite ou bien vous aurez l'erreur suivante :
-<screen><computeroutput>ERROR: function declared to return emp returns varchar instead of text at column 1</computeroutput></screen>
- </para>
- </listitem>
- </itemizedlist>
- </para>
-
- <para>
- Un autre façon de définir la même fonction est :
-
- <programlisting>CREATE FUNCTION nouveau_emp() RETURNS emp AS $$
- SELECT ROW('Aucun', 1000.0, 25, '(2,2)')::emp;
-$$ LANGUAGE SQL;</programlisting>
-
- Ici, nous écrivons un <command>SELECT</command> qui renvoie seulement une
- colonne du bon type composite. Ceci n'est pas vraiment meilleur dans
- cette situation mais c'est une alternative pratique dans certains cas
- — par exemple, si nous avons besoin de calculer le résultat en
- appelant une autre fonction qui renvoie la valeur composite désirée.
- </para>
-
- <para>
- Nous pourrions appeler cette fonction directement de deux façons :
-
- <screen>SELECT nouveau_emp();
-
- nouveau_emp
---------------------------
- (None,1000.0,25,"(2,2)")
-
-SELECT * FROM nouveau_emp();
-
- nom | salaire | age | cubicle
--------+---------+-----+---------
- Aucun | 1000.0 | 25 | (2,2)
- </screen>
-
- La deuxième façon est décrite plus complètement dans la <xref
- linkend="xfunc-sql-table-functions"/>.
- </para>
-
- <para>
- Quand vous utilisez une fonction qui renvoie un type composite, vous
- pourriez vouloir seulement un champ (attribut) depuis ce résultat. Vous
- pouvez le faire avec cette syntaxe :
-
-<screen>SELECT (nouveau_emp()).nom;
-
- nom
-------
- None
-</screen>
-
- Les parenthèses supplémentaires sont nécessaires pour éviter une erreur de
- l'analyseur. Si vous essayez de le faire sans, vous obtiendrez quelque chose
- comme ceci :
- <screen>SELECT nouveau_emp().nom;
-ERROR: syntax error at or near "."
-LINE 1: SELECT nouveau_emp().nom;
- ^
-</screen>
- </para>
-
- <para>
- Une autre option est d'utiliser la notation fonctionnelle pour extraire un
- attribut. Une manière simple d'expliquer cela est de dire que nous pouvons
- échanger les notations <literal>attribut(table)</literal> et
- <literal>table.attribut</literal>.
-
-<screen>SELECT nom(nouveau_emp());
-
- name
-------
- None
-</screen>
-
-<screen>-- C'est la même chose que
--- SELECT emp.nom AS leplusjeune FROM emp WHERE emp.age < 30;
-
-SELECT nom(emp) AS leplusjeune FROM emp WHERE age(emp) < 30;
-
- leplusjeune
--------------
- Sam
- Andy
-</screen>
- </para>
-
- <tip>
- <para>
- L'équivalence entre la notation fonctionnelle et la notation d'attribut
- rend possible l'utilisation de fonctions sur des types composites pour
- émuler les <quote>champs calculés</quote>.
- <indexterm>
- <primary>champ calculé</primary>
- </indexterm>
- <indexterm>
- <primary>field</primary>
- <secondary>computed</secondary>
- </indexterm>
- Par exemple, en utilisant la définition précédente pour
- <literal>double_salaire(emp)</literal>, nous pouvons écrire
-<screen>SELECT emp.nom, emp.double_salaire FROM emp;
-</screen>
-
- Une application utilisant ceci n'aurait pas besoin d'être consciente
- directement que <literal>double_salaire</literal> n'est pas une vraie colonne
- de la table (vous pouvez aussi émuler les champs calculés avec des
- vues).
- </para>
- </tip>
-
- <para>
- Une autre façon d'utiliser une fonction renvoyant un type composite est
- de l'appeler comme une fonction de table, comme décrit dans la <xref
- linkend="xfunc-sql-table-functions"/>.
- </para>
- </sect2>
-
- <sect2 id="xfunc-output-parameters">
- <title>Fonctions <acronym>SQL</acronym> avec des paramètres en sortie</title>
-
- <indexterm>
- <primary>fonction</primary>
- <secondary>paramètre en sortie</secondary>
- </indexterm>
-
- <para>
- Une autre façon de décrire les résultats d'une fonction est de la
- définir avec des <firstterm>paramètres en sortie</firstterm> comme dans cet
- exemple :
-
-<screen>CREATE FUNCTION ajoute (IN x int, IN y int, OUT sum int)
-AS 'SELECT $1 + $2'
-LANGUAGE SQL;
-
-SELECT ajoute(3,7);
- ajoute
---------
- 10
-(1 row)
-</screen>
-
- Ceci n'est pas vraiment différent de la version d'<literal>ajoute</literal>
- montrée dans la <xref linkend="xfunc-sql-base-functions"/>. La vraie valeur
- des paramètres en sortie est qu'ils fournissent une façon agréable de
- définir des fonctions qui renvoient plusieurs colonnes. Par exemple,
-
-<screen>CREATE FUNCTION ajoute_n_produit (x int, y int, OUT sum int, OUT product int)
-AS 'SELECT $1 + $2, $1 * $2'
-LANGUAGE SQL;
-
- SELECT * FROM sum_n_product(11,42);
- sum | product
------+---------
- 53 | 462
-(1 row)
-</screen>
-
- Ce qui est arrivé ici est que nous avons créé un type composite anonyme
- pour le résultat de la fonction. L'exemple ci-dessus a le même résultat
- final que
-
-<screen>CREATE TYPE produit_ajoute AS (somme int, produit int);
-
-CREATE FUNCTION ajoute_n_produit (int, int) RETURNS produit_ajoute
-AS 'SELECT $1 + $2, $1 * $2'
-LANGUAGE SQL;
-</screen>
-
- mais ne pas avoir à s'embêter avec la définition séparée du type
- composite est souvent agréable.
- </para>
-
- <para>
- Notez que les paramètres en sortie ne sont pas inclus dans la liste
- d'arguments lors de l'appel d'une fonction de ce type en SQL. Ceci
- parce que <productname>PostgreSQL</productname> considère seulement les
- paramètres en entrée pour définir la signature d'appel de la fonction.
- Cela signifie aussi que seuls les paramètres en entrée sont importants
- lors de références de la fonction pour des buts comme sa suppression.
- Nous pouvons supprimer la fonction ci-dessus avec l'un des deux appels
- ci-dessous :
-
-<screen>DROP FUNCTION ajoute_n_produit (x int, y int, OUT somme int, OUT produit int);
-DROP FUNCTION ajoute_n_produit (int, int);
-</screen>
- </para>
-
- <para>
- Les paramètres peuvent être marqués comme <literal>IN</literal> (par défaut),
- <literal>OUT</literal> ou <literal>INOUT</literal> ou
- <literal>VARIADIC</literal>.
- Un paramètre <literal>INOUT</literal>
- sert à la fois de paramètre en entrée (il fait partie de la liste
- d'arguments en appel) et comme paramètre de sortie (il fait partie du
- type d'enregistrement résultat).
- <literal>VARIADIC</literal> parameters are input parameters, but are treated
- specially as described next.
- </para>
- </sect2>
-
- <sect2 id="xfunc-sql-variadic-functions">
- <title>Fonctions <acronym>SQL</acronym> avec un nombre variables d'arguments</title>
-
- <indexterm>
- <primary>fonction</primary>
- <secondary>variadic</secondary>
- </indexterm>
-
- <indexterm>
- <primary>fonction variadic</primary>
- </indexterm>
-
- <para>
- Les fonctions <acronym>SQL</acronym> peuvent accepter un nombre variable
- d'arguments à condition que tous les arguments <quote>optionnels</quote>
- sont du même type. Les arguments optionnels seront passés à la fonction
- sous forme d'un tableau. La fonction est déclarée en marquant le dernier
- paramètre comme <literal>VARIADIC</literal> ; ce paramètre doit être
- déclaré de type tableau. Par exemple :
-
-<screen>
-CREATE FUNCTION mleast(VARIADIC numeric[]) RETURNS numeric AS $$
- SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
-$$ LANGUAGE SQL;
-
-SELECT mleast(10, -1, 5, 4.4);
- mleast
---------
- -1
-(1 row)
-</screen>
-
- En fait, tous les arguments à la position ou après la position de
- l'argument <literal>VARIADIC</literal> sont emballés dans un tableau à
- une dimension, comme si vous aviez écrit
-
-<screen>
-SELECT mleast(ARRAY[10, -1, 5, 4.4]); -- doesn't work
-</screen>
-
- Vous ne pouvez pas vraiment écrire cela, ou tout du moins cela ne
- correspondra pas à la définition de la fonction. Un paramètre marqué
- <literal>VARIADIC</literal> correspond à une ou plusieurs occurrences
- de son type d'élément, et non pas de son propre type.
- </para>
-
- <para>
- Sometimes it is useful to be able to pass an already-constructed array
- to a variadic function; this is particularly handy when one variadic
- function wants to pass on its array parameter to another one. You can
- do that by specifying <literal>VARIADIC</literal> in the call:
-
-<screen>
-SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
-</screen>
-
- This prevents expansion of the function's variadic parameter into its
- element type, thereby allowing the array argument value to match
- normally. <literal>VARIADIC</literal> can only be attached to the last
- actual argument of a function call.
- </para>
- </sect2>
-
- <sect2 id="xfunc-sql-parameter-defaults">
- <title>Fonctions SQL <acronym>SQL</acronym> avec des valeurs par défaut
- pour les arguments</title>
-
- <indexterm>
- <primary>fonction</primary>
- <secondary>valeurs par défaut pour les arguments</secondary>
- </indexterm>
-
- <para>
- Les fonctions peuvent être déclarées avec des valeurs par défaut pour
- certains des paramètres en entrée ou pour tous. Les valeurs par défaut
- sont insérées quand la fonction est appelée avec moins d'arguments que
- à priori nécessaires. Comme les arguments peuvent seulement être omis
- à partir de la fin de la liste des arguments, tous les paramètres après
- un paramètres disposant d'une valeur par défaut disposeront eux-aussi
- d'une valeur par défaut.
- </para>
-
- <para>
- Par exemple :
-<screen>
-CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3)
-RETURNS int
-LANGUAGE SQL
-AS $$
- SELECT $1 + $2 + $3;
-$$;
-
-SELECT foo(10, 20, 30);
- foo
------
- 60
-(1 row)
-
-SELECT foo(10, 20);
- foo
------
- 33
-(1 row)
-
-SELECT foo(10);
- foo
------
- 15
-(1 row)
-
-SELECT foo(); -- échec car il n'y a pas de valeur par défaut pour le premier argument
-ERROR: function foo() does not exist
-</screen>
- Le signe <literal>=</literal> peut aussi être utilisé à la place du mot clé
- <literal>DEFAULT</literal>,
- </para>
- </sect2>
-
- <sect2 id="xfunc-sql-table-functions">
- <title>Fonctions <acronym>SQL</acronym> comme sources de table</title>
-
- <para>
- Toutes les fonctions SQL peuvent être utilisées dans la clause
- <literal>FROM</literal> d'une requête mais ceci est particulièrement utile pour les
- fonctions renvoyant des types composite. Si la fonction est définie pour
- renvoyer un type de base, la fonction table produit une table d'une seule
- colonne. Si la fonction est définie pour renvoyer un type composite, la
- fonction table produit une colonne pour chaque attribut du type composite.
- </para>
-
- <para>
- Voici un exemple :
-
-<screen>CREATE TABLE foo (fooid int, foosousid int, foonom text);
-INSERT INTO foo VALUES (1, 1, 'Joe');
-INSERT INTO foo VALUES (1, 2, 'Ed');
-INSERT INTO foo VALUES (2, 1, 'Mary');
-
-CREATE FUNCTION recupfoo(int) RETURNS foo AS $$
- SELECT * FROM foo WHERE fooid = $1;
-$$ LANGUAGE SQL;
-
-SELECT *, upper(foonom) FROM recupfoo(1) AS t1;
-
- fooid | foosubid | foonom | upper
--------+----------+--------+-------
- 1 | 1 | Joe | JOE
-(1 row)
-</screen>
-
- Comme le montre cet exemple, nous pouvons travailler avec les colonnes du
- résultat de la fonction comme s'il s'agissait des colonnes d'une table
- normale.
-</para>
-
- <para>
- Notez que nous n'obtenons qu'une ligne comme résultat de la fonction. Ceci
- parce que nous n'avons pas utilisé l'instruction <literal>SETOF</literal>. Cette
- instruction est décrite dans la prochaine section.
- </para>
- </sect2>
-
- <sect2 id="xfunc-sql-functions-returning-set">
- <title>Fonctions <acronym>SQL</acronym> renvoyant un ensemble </title>
-
- <indexterm>
- <primary>fonction</primary>
- <secondary>avec SETOF</secondary>
- </indexterm>
-
- <para>
- Quand une fonction SQL est déclarée renvoyer un <literal>SETOF
- <replaceable>un_type</replaceable></literal>, la requête finale de
- la fonction est complètement exécutée et chaque ligne extraite est renvoyée
- en tant qu'élément de l'ensemble résultat.
- </para>
-
- <para>
- Cette caractéristique est normalement utilisée lors de l'appel d'une
- fonction dans une clause <literal>FROM</literal>. Dans ce cas, chaque ligne
- renvoyée par la fonction devient une ligne de la table vue par la requête.
- Par exemple, supposons que la table <literal>foo</literal> ait le même contenu que
- précédemment et écrivons :
-
-<programlisting>CREATE FUNCTION recupfoo(int) RETURNS SETOF foo AS $$
- SELECT * FROM foo WHERE fooid = $1;
-$$ LANGUAGE SQL;
-
-SELECT * FROM recupfoo(1) AS t1;
-</programlisting>
-
- Alors nous obtenons :
-<screen> fooid | foosousid | foonom
--------+-----------+--------
- 1 | 1 | Joe
- 1 | 2 | Ed
-(2 rows)
-</screen>
- </para>
-
- <para>
- Il est aussi possible de renvoyer plusieurs lignes avec les colonnes
- définies par des paramètres en sortie, comme ceci :
-
-<programlisting>
-CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int) RETURNS SETOF record AS $$
- SELECT $1 + tab.y, tab.y * tab.y FROM tab;
-$$ LANGUAGE SQL;
-</programlisting>
-
- Le point clé ici est que vous devez écrire <literal>RETURNS SETOF
- record</literal> pour indiquer que la fonction renvoie plusieurs lignes
- et non pas une seule. S'il n'y a qu'un paramètre en sortie, indiquez le
- type de paramètre plutôt que <type>record</type>.
- </para>
-
- <para>
- Actuellement, les fonctions renvoyant des ensembles peuvent aussi être
- appelées dans la liste du select d'une requête. Pour chaque ligne générée
- par la requête, la fonction renvoyant un ensemble est appelée et une ligne
- est générée pour chaque élément de l'ensemble résultat. Notez cependant que
- cette fonctionnalité est déconseillée et pourra être supprimée dans une
- version future. Voici un exemple de fonction renvoyant un ensemble à
- partir de la liste d'un SELECT :
-
-<screen>CREATE FUNCTION listeenfant(text) RETURNS SETOF text AS $$
- SELECT nom FROM noeuds WHERE parent = $1
-$$ LANGUAGE SQL;
-
-SELECT * FROM noeuds;
- nom | parent
---------------+--------
- Haut |
- Enfant1 | Haut
- Enfant2 | Haut
- Enfant3 | Haut
- Sous-Enfant1 | Enfant1
- Sous-Enfant2 | Enfant1
-(6 rows)
-
-SELECT listeenfant('Haut');
- listeenfant
---------------
- Enfant1
- Enfant2
- Enfant3
-(3 rows)
-
-SELECT nom, listeenfant(nom) FROM noeuds;
- nom | listeenfant
----------+--------------
- Haut | Enfant1
- Haut | Enfant2
- Haut | Enfant3
- Enfant1 | Sous-Enfant1
- Enfant1 | Sous-Enfant2
-(5 rows)
-</screen>
-
- Notez, dans le dernier <command>SELECT</command>, qu'aucune ligne n'est
- renvoyée pour <literal>Enfant2</literal>, <literal>Enfant3</literal>, etc. C'est parce
- que la fonction <function>listeenfant</function> renvoie un ensemble vide
- pour ces arguments et ainsi aucune ligne n'est générée.
- </para>
-
- <note>
- <para>
- Si la dernière commande d'une fonction est <command>INSERT</command>,
- <command>UPDATE</command> ou <command>DELETE</command> avec une clause
- <literal>RETURNING</literal>, cette commande sera toujours exécutée
- jusqu'à sa fin, même si la fonction n'est pas déclarée avec
- <literal>SETOF</literal> ou que la requête appelante ne renvoie pas toutes
- les lignes résultats. Toutes les lignes supplémentaires produites par la
- clause <literal>RETURNING</literal> sont silencieusement abandonnées mais
- les modifications de table sont pris en compte (et sont toutes terminées
- avant que la fonction ne se termine).
- </para>
- </note>
- </sect2>
-
- <sect2 id="xfunc-sql-functions-returning-table">
- <title>Fonctions <acronym>SQL</acronym> renvoyant <literal>TABLE</literal></title>
-
- <indexterm>
- <primary>fonction</primary>
- <secondary>RETURNS TABLE</secondary>
- </indexterm>
-
- <para>
- Il existe une autre façon de déclarer une fonction comme renvoyant un
- ensemble de données. Cela passe par la syntaxe <literal>RETURNS
- TABLE(<replaceable>colonnes</replaceable>)</literal>. C'est équivalent
- à utiliser un ou plusieurs paramètres <literal>OUT</literal> et à marquer
- la fonction comme renvoyant un <literal>SETOF record</literal> (ou
- <literal>SETOF</literal> d'un type simple en sortie, comme approprié).
- Cette notation est indiquée dans les versions récentes du standard SQL
- et, du coup, devrait être plus portable que <literal>SETOF</literal>.
- </para>
-
- <para>
- L'exemple précédent, sum-and-product, peut se faire aussi de la façon
- suivante :
-
-<programlisting>
-CREATE FUNCTION sum_n_product_with_tab (x int) RETURNS TABLE(sum int, product int) AS $$
- SELECT $1 + tab.y, $1 * tab.y FROM tab;
-$$ LANGUAGE SQL;
-</programlisting>
-
- Il n'est pas autorisé d'utiliser explicitement des paramètres
- <literal>OUT</literal> ou <literal>INOUT</literal> avec la notation
- <literal>RETURNS TABLE</literal> — vous devez indiquer toutes les
- colonnes en sortie dans la liste <literal>TABLE</literal>.
- </para>
- </sect2>
-
- <sect2>
- <title>Fonctions <acronym>SQL</acronym> polymorphes</title>
-
- <para>
- Les fonctions <acronym>SQL</acronym> peuvent être déclarées pour accepter
- et renvoyer les types <quote>polymorphe</quote> <type>anyelement</type>,
- <type>anyarray</type>, <type>anynonarray</type> et <type>anyenum</type>.
- Voir la <xref linkend="extend-types-polymorphic"/> pour une
- explication plus approfondie. Voici une fonction polymorphe
- <function>cree_tableau</function> qui construit un tableau à partir de
- deux éléments de type arbitraire :
-
-<screen>CREATE FUNCTION cree_tableau(anyelement, anyelement) RETURNS anyarray AS $$
- SELECT ARRAY[$1, $2];
-$$ LANGUAGE SQL;
-SELECT cree_tableau(1, 2) AS tableau_entier, cree_tableau('a'::text, 'b') AS
-tableau_texte;
-
- tableau_entier | tableau_texte
-----------------+---------------
- {1,2} | {a,b}
-(1 row)
-</screen>
- </para>
-
- <para>
- Notez l'utilisation du transtypage <literal>'a'::text</literal> pour
- spécifier le type <type>text</type> de l'argument. Ceci est nécessaire si
- l'argument est une chaîne de caractères car, autrement, il serait traité
- comme un type <type>unknown</type>, et un tableau de type
- <type>unknown</type> n'est pas un type valide. Sans le transtypage, vous
- obtiendrez ce genre d'erreur :
-
-<screen><computeroutput>ERROR: could not determine polymorphic type because input is UNKNOWN</computeroutput></screen>
- </para>
-
- <para>
- Il est permis d'avoir des arguments polymorphes avec un type de renvoi
- fixe, mais non l'inverse. Par exemple :
-
-<screen>CREATE FUNCTION est_plus_grand(anyelement, anyelement) RETURNS bool AS $$
- SELECT $1 > $2;
-$$ LANGUAGE SQL;
-
-SELECT est_plus_grand(1, 2);
- est_plus_grand
-----------------
- f
-(1 row)
-
-CREATE FUNCTION fonction_invalide() RETURNS anyelement AS $$
- SELECT 1;
-$$ LANGUAGE SQL;
-ERROR: cannot determine result datatype
-DETAIL: A function returning a polymorphic type must have at least one
-polymorphic argument.
-</screen>
- </para>
-
- <para>
- Le polymorphisme peut être utilisé avec les fonctions qui ont des
- arguments en sortie. Par exemple :
-<screen>CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray)
-AS 'select $1, array[$1,$1]' LANGUAGE SQL;
-
-SELECT * FROM dup(22);
- f2 | f3
-----+---------
- 22 | {22,22}
-(1 row)
-</screen>
- </para>
-
- <para>
- Le polymorphisme peut aussi être utilisé avec des fonctions variadic.
- Par exemple :
-<screen>
-CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
- SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
-$$ LANGUAGE SQL;
-
-SELECT anyleast(10, -1, 5, 4);
- anyleast
-----------
- -1
-(1 row)
-
-SELECT anyleast('abc'::text, 'def');
- anyleast
-----------
- abc
-(1 row)
-
-CREATE FUNCTION concat(text, VARIADIC anyarray) RETURNS text AS $$
- SELECT array_to_string($2, $1);
-$$ LANGUAGE SQL;
-
-SELECT concat('|', 1, 4, 2);
- concat
---------
- 1|4|2
-(1 row)
-</screen>
- </para>
- </sect2>
- </sect1>
-
- <sect1 id="xfunc-overload">
- <title>Surcharge des fonctions</title>
-
- <indexterm zone="xfunc-overload">
- <primary>surcharge</primary>
- <secondary>fonctions</secondary>
- </indexterm>
-
- <para>
- Plusieurs fonctions peuvent être définies avec le même nom SQL à condition
- que les arguments soient différents. En d'autres termes, les noms de
- fonction peuvent être <firstterm>surchargés</firstterm>. Quand une
- requête est exécutée, le serveur déterminera la fonction à appeler à
- partir des types de données des arguments et du nombre d'arguments. La
- surcharge peut aussi être utilisée pour simuler des fonctions avec un
- nombre variable d'arguments jusqu'à un nombre maximum fini.
- </para>
-
- <para>
- Lors de la création d'une famille de fonctions surchargées, vous devriez
- être attentif à ne pas créer d'ambiguïtés. Par exemple, avec les
- fonctions :
- <programlisting>CREATE FUNCTION test(int, real) RETURNS ...
-CREATE FUNCTION test(smallint, double precision) RETURNS ...</programlisting>
- Savoir quelle fonction sera appelée avec une entrée triviale comme
- <literal>test(1, 1.5)</literal> n'est pas immédiatement clair. Les
- règles de résolution actuellement implémentées sont décrites dans le
- <xref linkend="typeconv"/> mais il est déconseillé de concevoir un
- système qui serait basé subtilement sur ce comportement.
- </para>
-
- <para>
- Une fonction qui prend un seul argument d'un type composite devrait
- généralement ne pas avoir le même nom que tout attribut (champ) de
- ce type. Rappellez-vous que <literal>attribut(table)</literal> est
- considéré comme équivalent à <literal>table.attribut</literal>. Dans
- le cas où il existe une ambiguïté entre une fonction sur un type
- composite et sur un attribut d'un type composite, l'attribut sera
- toujours utilisé. Il est possible de contourner ce choix en
- qualifiant le nom de la fonction avec celui du schéma (c'est-à-dire
- <literal>schema.fonction(table)</literal>) mais il est préférable
- d'éviter le problème en ne choisissant aucun nom conflictuel.
- </para>
-
- <para>
- Un autre conflit possible se trouve entre les fonctions variadic et les
- autres. En fait, il est possible de créer à la fois
- <literal>foo(numeric)</literal> et <literal>foo(VARIADIC numeric[])</literal>.
- Dans ce cas, il n'est pas simple de savoir lequel sera sélectionné lors
- d'un appel avec un seul argument numérique, par exemple
- <literal>foo(10.1)</literal>. La règle est que la fonction apparaissant
- plsu tôt dans le chemin des schémas est utilisé. De même, si les deux
- fonctions sont dans le même schéma, la non variadic est préféré.
- </para>
-
- <para>
- Lors de la surcharge de fonctions en langage C, il existe une
- contrainte supplémentaire : le nom C de chaque fonction dans la
- famille des fonctions surchargées doit être différent des noms C de
- toutes les autres fonctions, soit internes soit chargées dynamiquement
- Si cette règle est violée, le comportement n'est pas portable. Vous
- pourriez obtenir une erreur de l'éditeur de lien ou une des fonctions
- sera appelée (habituellement l'interne). L'autre forme de clause
- <literal>AS</literal> pour la commande SQL <command>CREATE
- FUNCTION</command> découple le nom de la fonction SQL à partir du
- nom de la fonction dans le code source C. Par exemple :
-<programlisting>CREATE FUNCTION test(int) RETURNS int
- AS '<replaceable>filename</replaceable>', 'test_1arg'
-LANGUAGE C;
-CREATE FUNCTION test(int, int) RETURNS int
- AS '<replaceable>filename</replaceable>', 'test_2arg'
-LANGUAGE C;
-</programlisting>
- Les noms des fonctions C reflètent ici une des nombreuses conventions
- possibles.
- </para>
- </sect1>
-
- <sect1 id="xfunc-volatility">
- <title>Catégories de volatilité des fonctions</title>
-
- <indexterm zone="xfunc-volatility">
- <primary>volatilité</primary>
- <secondary>fonctions</secondary>
- </indexterm>
- <indexterm zone="xfunc-volatility">
- <primary>VOLATILE</primary>
- </indexterm>
- <indexterm zone="xfunc-volatility">
- <primary>STABLE</primary>
- </indexterm>
- <indexterm zone="xfunc-volatility">
- <primary>IMMUTABLE</primary>
- </indexterm>
-
- <para>
- Chaque fonction a une classification de volatilité
- (<firstterm>volatility</firstterm>) comprenant
- <literal>VOLATILE</literal>, <literal>STABLE</literal> ou <literal>IMMUTABLE</literal>.
- <literal>VOLATILE</literal> est la valeur par défaut si la commande
- <xref linkend="sql-createfunction" endterm="sql-createfunction-title"/> ne
- spécifie pas de catégorie. La catégorie de volatilité est une promesse
- à l'optimiseur sur le comportement de la fonction :
-
- <itemizedlist>
- <listitem>
- <para>
- Une fonction <literal>VOLATILE</literal> peut tout faire, y compris modifier
- la base de données. Elle peut renvoyer différents
- résultats sur des appels successifs avec les mêmes arguments.
- L'optimiseur ne fait aucune supposition sur le comportement de telles
- fonctions. Une requête utilisant une fonction volatile ré-évaluera la
- fonction à chaque ligne où sa valeur est nécessaire.
- </para>
- </listitem>
- <listitem>
- <para>
- Une fonction <literal>STABLE</literal> ne peut pas modifier la base de
- données et est garantie de renvoyer les mêmes résultats si elle est
- appelée avec les mêmes arguments pour toutes les lignes à l'intérieur
- d'une même instruction. Cette catégorie permet à l'optimiseur d'optimiser
- plusieurs appels de la fonction dans une seule requête. En particulier,
- vous pouvez utiliser en toute sécurité une expression contenant une
- telle fonction dans une condition de parcours d'index (car un parcours
- d'index évaluera la valeur de la comparaison une seule fois, pas une
- fois pour chaque ligne, utiliser une fonction <literal>VOLATILE</literal> dans
- une condition de parcours d'index n'est pas valide).
- </para>
- </listitem>
- <listitem>
- <para>
- Une fonction <literal>IMMUTABLE</literal> ne peut pas modifier la base de
- données et est garantie de toujours renvoyer les mêmes résultats si
- elle est appelée avec les mêmes arguments. Cette catégorie permet à
- l'optimiseur de pré-évaluer la fonction quand une requête l'appelle
- avec des arguments constants. Par exemple, une requête comme
- <literal>SELECT ... WHERE x = 2 + 2</literal> peut être simplifiée pour
- obtenir <literal>SELECT ... WHERE x = 4</literal> car la fonction sous-jacente
- de l'opérateur d'addition est indiquée <literal>IMMUTABLE</literal>.
- </para>
- </listitem>
- </itemizedlist>
-
- </para>
-
- <para>
- Pour une meilleure optimisation des résultats, vous devez mettre un label
- sur les fonctions avec la catégorie la plus volatile valide pour elles.
- </para>
-
- <para>
- Toute fonction avec des effets de bord <emphasis>doit</emphasis> être indiquée
- comme <literal>VOLATILE</literal>, de façon à ce que les appels ne puissent pas
- être optimisés. Même une fonction sans effets de bord doit être indiquée
- comme <literal>VOLATILE</literal> si sa valeur peut changer à l'intérieur
- d'une seule requête ; quelques exemples sont <literal>random()</literal>,
- <literal>currval()</literal>, <literal>timeofday()</literal>.
- </para>
-
- <para>
- Un autre exemple important est que la famille de fonctions
- <function>current_timestamp</function> est qualifiée comme
- <literal>STABLE</literal> car leurs valeurs ne changent pas à l'intérieur
- d'une transaction.
- </para>
-
- <para>
- Il y a relativement peu de différences entre les catégories
- <literal>STABLE</literal> et <literal>IMMUTABLE</literal> en considérant les requêtes
- interactives qui sont planifiées et immédiatement exécutées : il
- importe peu que la fonction soit exécutée une fois lors de la
- planification ou une fois au lancement de l'exécution de la requête mais
- cela fait une grosse différence si le plan est sauvegardé et utilisé plus
- tard. Placer un label <literal>IMMUTABLE</literal> sur une fonction quand elle
- ne l'est pas vraiment pourrait avoir comme conséquence de la considérer
- prématurément comme une constante lors de la planification et résulterait en une valeur
- erronée lors d'une utilisation ultérieure de ce plan d'exécution.
- C'est un danger qui arrive lors de l'utilisattion d'instructions préparées
- ou avec l'utilisation de langages de fonctions mettant les plans d'exécutions
- en cache (comme
- <application>PL/pgSQL</application>).
- </para>
-
- <para>
- Pour les fonctions écrites en SQL ou dans tout autre langage de procédure
- standard, la catégorie de volatibilité détermine une deuxième propriété
- importante, à savoir la visibilité de toute modification de données
- effectuées par la commande SQL qui a appelé la fonction. Une fonction
- <literal>VOLATILE</literal> verra les changements, une fonction
- <literal>STABLE</literal> ou <literal>IMMUTABLE</literal> ne les verra pas.
- Ce comportement est implantée en utilisant le comportement par images de
- MVCC (voir <xref linkend="mvcc"/>) : les fonctions
- <literal>STABLE</literal> et <literal>IMMUTABLE</literal> utilisent une
- image établie au lancement de la requête appelante alors que les fonctions
- <literal>VOLATILE</literal> obtiennent une image fraiche au début de chaque
- requête qu'elles exécutent.
- </para>
-
- <note>
- <para>
- Les fonctions écrites en C peuvent gérer les images de la façon qu'elles
- le souhaitent, mais il est préférable de coder les fonctions C de la même
- façon.
- </para>
- </note>
-
- <para>
- À cause du comportement à base d'images, une fonction contenant seulement des commandes
- <command>SELECT</command> peut être indiquée <literal>STABLE</literal> en toute sécurité
- même s'il sélectionne des données à partir de tables qui pourraient
- avoir subi des modifications entre temps par des requêtes concurrentes.
- <productname>PostgreSQL</productname> exécutera toutes les commandes d'une fonction
- <literal>STABLE</literal> en utilisant l'image établie par la requête appelante et
- n'aura qu'une vision figée de la base de données au cours de la requête.
- </para>
-
- <para>
- Ce même comportement d'images est utilisé pour les commandes
- <command>SELECT</command> à l'intérieur de fonctions <literal>IMMUTABLE</literal>. Il
- est généralement déconseillé de sélectionner des tables de la base de
- données à l'intérieur de fonctions <literal>IMMUTABLE</literal> car
- l'immutabilité sera rompue si le contenu de la table change. Néanmoins,
- <productname>PostgreSQL</productname> ne vous force pas à ne pas le faire.
- </para>
-
- <para>
- Une erreur commune est de placer un label sur une fonction
- <literal>IMMUTABLE</literal> quand son résultat dépend d'un paramètre de
- configuration. Par exemple, une fonction qui manipule des types date/heure
- pourrait bien avoir des résultats dépendant du paramètre
- <xref linkend="guc-timezone"/>. Pour être sécurisées, de telles
- fonctions devraient avoir le label <literal>STABLE</literal> à la place.
- </para>
-
- <note>
- <para>
- Avant <productname>PostgreSQL</productname> version 8.0, le prérequis
- que les fonctions <literal>STABLE</literal> et
- <literal>IMMUTABLE</literal> ne pouvaient pas modifier la base de données
- n'était pas contraint par le système. Les versions 8.0 et ultérieures le
- contraignent en
- réclamant que les fonctions SQL et les fonctions de langages de
- procédures de ces catégories ne contiennent pas de commandes SQL autre
- que <command>SELECT</command> (ceci n'a pas été complètement testé car de
- telles fonctions pourraient toujours appeler des fonctions
- <literal>VOLATILE</literal> qui modifient la base de données. Si vous le
- faites, vous trouverez que la fonction <literal>STABLE</literal> ou
- <literal>IMMUTABLE</literal> n'est pas au courant des modifications
- effectuées sur la base de données par la fonction appelée, car elles
- sont cachées depuis son image).
- </para>
- </note>
- </sect1>
-
- <sect1 id="xfunc-pl">
- <title>Fonctions en langage de procédures</title>
-
- <para>
- <productname>PostgreSQL</productname> autorise l'écriture de fonctions
- définies par l'utilisateur dans d'autres langages que SQL et C. Ces autres
- langages sont appelés des <firstterm>langages de procédure</firstterm>
- (<acronym>PL</acronym>). Les langages de procédures ne sont pas compilés dans le
- serveur <productname>PostgreSQL</productname> ; ils sont fournis comme
- des modules chargeables. Voir le <xref linkend="xplang"/> et les chapitres
- suivants pour plus d'informations.
- </para>
-
- <para>
- Il y a actuellement quatre langages de procédures disponibles dans la
- distribution <productname>PostgreSQL</productname> standard :
- <application>PL/pgSQL</application>, <application>PL/Tcl</application>,
- <application>PL/Perl</application> et <application>PL/Python</application>.
-
- Référez-vous au <xref linkend="xplang"/> pour plus d'informations. D'autres
- langages peuvent être définis par les utilisateurs. Les bases du
- développement d'un nouveau langage de procédures sont traitées dans le <xref
- linkend="plhandler"/>.
- </para>
-
- </sect1>
-
- <sect1 id="xfunc-internal">
- <title>Fonctions internes</title>
-
- <indexterm zone="xfunc-internal"><primary>fonction</primary><secondary>interne</secondary></indexterm>
-
- <para>
- Les fonctions internes sont des fonctions écrites en C qui ont été liées de
- façon statique dans le serveur <productname>PostgreSQL</productname>. Le
- <quote>corps</quote> de la définition de la fonction spécifie le nom en
- langage C de la fonction, qui n'est pas obligatoirement le même que le nom
- déclaré pour l'utilisation en SQL (pour des raisons de rétro compatibilité,
- un corps vide est accepté pour signifier que le nom de la fonction en
- langage C est le même que le nom SQL).
-
- </para>
-
- <para>
- Normalement, toutes les fonctions internes présentes dans le serveur sont
- déclarées pendant l'initialisation du groupe de base de données
- (<command>initdb</command>) mais un utilisateur peut utiliser la commande
- <command>CREATE FUNCTION</command> pour créer des noms d'alias
- supplémentaires pour une fonction interne. Les fonctions internes sont
- déclarées dans la commande <command>CREATE FUNCTION</command> avec le nom
- de langage <literal>internal</literal>. Par exemple, pour créer un alias
- de la fonction <function>sqrt</function> :
-
-<programlisting>CREATE FUNCTION racine_carree(double precision) RETURNS double precision AS
-'dsqrt'
-LANGUAGE internal STRICT;
-</programlisting>
-
- (la plupart des fonctions internes doivent être déclarées
- <quote>STRICT</quote>)
- </para>
-
- <note>
- <para>
- Toutes les fonctions <quote>prédéfinies</quote> ne sont pas internes (au
- sens explicité ci-dessus). Quelques fonctions prédéfinies sont écrites en
- SQL.
- </para>
- </note>
- </sect1>
-
- <sect1 id="xfunc-c">
- <title>Fonctions en langage C</title>
-
- <indexterm zone="xfunc-c">
- <primary>fonction</primary>
- <secondary>définie par l'utilisateur</secondary>
- <tertiary>en C</tertiary>
- </indexterm>
-
- <para>
- Les fonctions définies par l'utilisateur peuvent être écrites en C (ou dans
- un langage pouvant être rendu compatible avec C, comme le C++). Ces fonctions
- sont compilées en objets dynamiques chargeables (encore appelés
- bibliothèques partagées) et sont chargées par le serveur à la demande. Cette
- caractéristique de chargement dynamique est ce qui distingue les fonctions
- en <quote>langage C</quote> des fonctions <quote>internes</quote> — les véritables
- conventions de codage sont essentiellement les mêmes pour les deux (c'est
- pourquoi la bibliothèque standard de fonctions internes est une source
- abondante d'exemples de code pour les fonctions C définies par
- l'utilisateur).
- </para>
-
- <para>
- Deux différentes conventions d'appel sont actuellement en usage pour les
- fonctions C. La plus récente, <quote>version 1</quote>,
- est indiquée en écrivant une macro d'appel
- <literal>PG_FUNCTION_INFO_V1()</literal> comme illustré ci-après. L'absence
- d'une telle macro indique une fonction écrite selon l'ancien style
- (<quote>version 0</quote>). Le nom de langage spécifié dans la commande
- <command>CREATE FUNCTION</command> est <literal>C</literal> dans les deux
- cas. Les fonctions suivant l'ancien style sont maintenant déconseillées en
- raison de problèmes de portabilité et d'un manque de fonctionnalité mais
- elles sont encore supportées pour des raisons de compatibilité.
- </para>
-
- <sect2 id="xfunc-c-dynload">
- <title>Chargement dynamique</title>
-
- <indexterm zone="xfunc-c-dynload">
- <primary>dynamic loading</primary>
- </indexterm>
-
- <para>
- La première fois qu'une fonction définie par l'utilisateur dans un fichier
- objet particulier chargeable est appelée dans une session, le chargeur
- dynamique charge ce fichier objet en mémoire de telle sorte que la fonction
- peut être appelée. La commande <command>CREATE FUNCTION</command> pour une
- fonction en C définie par l'utilisateur doit par conséquent spécifier deux
- éléments d'information pour la fonction : le nom du fichier objet
- chargeable et le nom en C (lien symbolique) de la fonction spécifique à
- appeler à l'intérieur de ce fichier objet. Si le nom en C n'est pas
- explicitement spécifié, il est supposé être le même que le nom de la
- fonction SQL.
- </para>
-
- <para>
- L'algorithme suivant, basé sur le nom donné dans la commande
- <command>CREATE FUNCTION</command>, est utilisé pour localiser le fichier
- objet partagé :
-
- <orderedlist>
- <listitem>
- <para>
- Si le nom est un chemin absolu, le fichier est chargé.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Si le nom commence par la chaîne <literal>$libdir</literal>, cette chaîne
- est remplacée par le nom du répertoire de la bibliothèque du paquetage
- <productname>PostgreSQL</productname>, qui est déterminé au moment de la
- compilation. <indexterm><primary>$libdir</primary></indexterm>
- </para>
- </listitem>
-
- <listitem>
- <para>
- Si le nom ne contient pas de partie répertoire, le fichier est recherché
- par le chemin spécifié dans la variable de configuration
- <xref linkend="guc-dynamic-library-path"/>.
- <indexterm><primary>dynamic_library_path</primary></indexterm>
- </para>
- </listitem>
-
- <listitem>
- <para>
- Dans les autres cas, (nom de fichier non trouvé dans le chemin ou ne
- contenant pas de partie répertoire non absolu), le chargeur dynamique
- essaiera d'utiliser le nom donné, ce qui échouera très vraisemblablement
- (dépendre du répertoire de travail en cours n'est pas fiable).
- </para>
- </listitem>
- </orderedlist>
-
- Si cette séquence ne fonctionne pas, l'extension pour les noms de fichier
- des bibliothèques partagées spécifique à la plateforme (souvent
- <filename>.so</filename>) est ajoutée au nom attribué et la séquence est à
- nouveau tentée. En cas de nouvel échec, le chargement échoue.
- </para>
-
- <para>
- Il est recommandé de localiser les bibliothèques partagées soit relativement
- à <literal>$libdir</literal> ou via le chemin dynamique des bibliothèques.
- Ceci simplifie les mises à jour de versions si la nouvelle installation est
- à un emplacement différent. Le répertoire actuel représenté par
- <literal>$libdir</literal> est trouvable avec la commande
- <literal>pg_config --pkglibdir</literal>.
- </para>
-
- <para>
- L'identifiant utilisateur sous lequel fonctionne le serveur
- <productname>PostgreSQL</productname> doit pouvoir suivre le chemin
- jusqu'au fichier que vous essayez de charger. Une erreur fréquente revient
- à définir le fichier ou un répertoire supérieur comme non lisible et/ou
- non exécutable par l'utilisateur <systemitem>postgres</systemitem>.
- </para>
-
- <para>
- Dans tous les cas, le nom de fichier donné dans la commande <command>CREATE
- FUNCTION</command> est enregistré littéralement dans les catalogues
- systèmes, de sorte que, si le fichier doit être à nouveau chargé, la même
- procédure sera appliquée.
- </para>
-
- <note>
- <para>
- <productname>PostgreSQL</productname> ne compilera pas une fonction C
- automatiquement. Le fichier objet doit être compilé avant d'être référencé
- dans une commande <command>CREATE FUNCTION</command>. Voir la <xref
- linkend="dfunc"/> pour des informations complémentaires.
- </para>
- </note>
-
- <indexterm zone="xfunc-c-dynload">
- <primary>bloc magique</primary>
- </indexterm>
-
- <para>
- Pour s'assurer qu'un fichier objet chargeable dynamiquement n'est pas chargé
- dans un serveur incompatible, <productname>PostgreSQL</productname> vérifie
- que le fichier contient un <quote>bloc magique</quote> avec un contenu
- approprié. Ceci permet au serveur de détecter les incompatibilités évidentes
- comme du code compilet pour une version majeure différente de
- <productname>PostgreSQL</productname>. Un bloc magique est requis à partir de
- <productname>PostgreSQL</productname> 8.2. Pour inclure un bloc magique,
- écrivez ceci dans un (et seulement un) des fichiers source du module, après
- avoir inclus l'en-tête <filename>fmgr.h</filename> :
-
-<programlisting>#ifdef PG_MODULE_MAGIC
-PG_MODULE_MAGIC;
-#endif
-</programlisting>
-
- Le test <literal>#ifdef</literal> peut être omis si le code n'a pas besoin
- d'être compilé avec des versions de <productname>PostgreSQL</productname>
- antérieures à la 8.2.
- </para>
-
- <para>
- Après avoir été utilisé pour la première fois, un fichier objet chargé
- dynamiquement est conservé en mémoire. Les futurs appels de fonction(s) dans
- ce fichier pendant la même session provoqueront seulement une légère
- surcharge due à la consultation d'une table de symboles. Si vous devez
- forcer le chargement d'un fichier objet, par exemple après une
- recompilation, utilisez la commande <xref
- linkend="sql-load" endterm="sql-load-title"/> ou commencez une
- nouvelle session.
- </para>
-
- <indexterm zone="xfunc-c-dynload">
- <primary>_PG_init</primary>
- </indexterm>
- <indexterm zone="xfunc-c-dynload">
- <primary>_PG_fini</primary>
- </indexterm>
- <indexterm zone="xfunc-c-dynload">
- <primary>fonction d'initialisation de la bibliothèque</primary>
- </indexterm>
- <indexterm zone="xfunc-c-dynload">
- <primary>fonction de terminaison de la bibliothèque</primary>
- </indexterm>
-
- <para>
- De façon optionnelle, un fichier chargé dynamiquement peut contenir des
- fonctions d'initialisation et de terminaison. Si le fichier inclut une
- fonction nommée <function>_PG_init</function>, cette fonction sera appelée
- immédiatement après le chargement du fichier. La fonction ne reçoit
- aucun paramètre et doit renvoyer void. Si le fichier inclut une fonction
- nommée <function>_PG_fini</function>, cette fonction sera appelée tout juste
- avant le déchargement du fichier. De la même façon, la fonction ne reçoit
- aucun paramètre et doit renvoyer void. Notez que <function>_PG_fini</function>
- sera seulement appelée lors du déchargement du fichier, pas au moment de la
- fin du processus. (Actuellement, un déchargement n'intervient que dans le
- contexte d'un re-chargement du fichier suite à la commande explicite
- <command>LOAD</command>.)
- </para>
-
- </sect2>
-
- <sect2 id="xfunc-c-basetype">
- <title>Types de base dans les fonctions en langage C</title>
-
- <indexterm zone="xfunc-c-basetype">
- <primary>type de données</primary>
- <secondary>organisation interne</secondary>
- </indexterm>
-
- <para>
- Pour savoir comment écrire des fonctions en langage C, vous devez savoir
- comment <productname>PostgreSQL</productname> représente en interne les
- types de données de base et comment elles peuvent être passés vers et
- depuis les fonctions. En interne, <productname>PostgreSQL</productname>
- considère un type de base comme un <quote>blob de mémoire</quote>. Les
- fonctions que vous définissez sur un type définissent à leur tour la façon
- que <productname>PostgreSQL</productname> opère sur lui. C'est-à-dire
- que <productname>PostgreSQL</productname> ne fera que conserver et retrouver
- les données sur le disque et utilisera votre fonction pour entrer, traiter
- et restituer les données.
- </para>
-
- <para>
- Les types de base peuvent avoir un des trois formats internes
- suivants :
- <itemizedlist>
- <listitem>
- <para>
- passage par valeur, longueur fixe ;
- </para>
- </listitem>
- <listitem>
- <para>
- passage par référence, longueur fixe ;
- </para>
- </listitem>
- <listitem>
- <para>
- passage par référence, longueur variable.
- </para>
- </listitem>
- </itemizedlist>
- </para>
-
- <para>
- Les types par valeur peuvent seulement avoir une longueur de 1, 2 ou 4
- octets (également 8 octets si <literal>sizeof(Datum)</literal> est de huit
- octets sur votre machine). Vous devriez être attentif lors de la
- définition de vos types de sorte à qu'ils aient la même taille sur toutes
- les architectures. Par exemple, le type <literal>long</literal> est
- dangereux car il a une taille de quatre octets sur certaines machines et
- huit octets sur d'autres, alors que le type <type>int</type> est de quatre
- octets sur la plupart des machines Unix. Une implémentation raisonnable du
- type <type>int4</type> sur une machine Unix pourrait être
-
- <programlisting>/* entier sur quatre octets, passé par valeur */
-typedef int int4;
-</programlisting>
-
- </para>
-
- <para>
- D'autre part, les types à longueur fixe d'une taille quelconque peuvent
- être passés par référence. Par exemple, voici l'implémentation d'un type
- <productname>PostgreSQL</productname> :
-
-<programlisting>/* structure de 16 octets, passée par référence */
-typedef struct
-{
- double x, y;
-} Point;
-</programlisting>
-
- Seuls des pointeurs vers de tels types peuvent être utilisés en les passant
- dans et hors des fonctions <productname>PostgreSQL</productname>. Pour
- renvoyer une valeur d'un tel type, allouez la quantité appropriée de
- mémoire avec <literal>palloc</literal>, remplissez la mémoire allouée et
- renvoyez un pointeur vers elle (de plus, si vous souhaitez seulement
- renvoyer la même valeur qu'un de vos arguments en entrée qui se trouve du
- même type, vous pouvez passer le <literal>palloc</literal>
- supplémentaire et simplement renvoyer le pointeur vers la valeur en
- entrée).
- </para>
-
- <para>
- Enfin, tous les types à longueur variable doivent aussi être passés par
- référence. Tous les types à longueur variable doivent commencer avec un
- champ d'une longueur d'exactement quatre octets et toutes les données
- devant être stockées dans ce type doivent être localisées dans la mémoire à
- la suite immédiate de ce champ longueur. Le champ longueur contient la
- longueur totale de la structure, c'est-à-dire incluant la longueur du
- champ longueur lui-même.
- </para>
-
- <warning>
- <para>
- Ne <emphasis>jamais</emphasis> modifier le contenu d'une valeur en entrée passée
- par référence. Si vous le faites, il y a de forts risques pour que
- vous réussissiez à corrompre les données sur disque car le pointeur que
- vous avez reçu pourrait bien pointer directement vers un tampon disque.
- La seule exception à cette règle est expliquée dans la <xref
- linkend="xaggr"/>.
- </para>
- </warning>
-
- <para>
- Comme exemple, nous pouvons définir le type <type>text</type> comme
- ceci :
-
-<programlisting>typedef struct {
- int4 length;
- char data[1];
-} text;
-</programlisting>
-
- Il est évident que le champ déclaré ici n'est pas assez long pour contenir
- toutes les chaînes possibles. Comme il est impossible de déclarer une
- structure de taille variable en <acronym>C</acronym>, nous nous appuyons
- sur le fait que le compilateur <acronym>C</acronym> ne vérifie pas la
- plage des indices de tableau. Nous allouons juste la quantité d'espace
- nécessaire et ensuite nous accédons au tableau comme s'il avait été déclaré
- avec la bonne longueur (c'est une astuce courante que vous pouvez trouver
- dans beaucoup de manuels de C).
- </para>
-
- <para>
- En manipulant les types à longueur variable, nous devons être attentifs à
- allouer la quantité correcte de mémoire et à fixer correctement le champ
- longueur. Par exemple, si nous voulons stocker 40 octets dans une structure
- <structname>text</structname>, nous devrions utiliser un fragment de code comme
- celui-ci :
-
-<programlisting><![CDATA[#include "postgres.h"
-...
-char buffer[40]; /* notre donnée source */
-...
-text *destination = (text *) palloc(VARHDRSZ + 40);
-destination->length = VARHDRSZ + 40;
-memcpy(destination->data, buffer, 40);
-...
-]]></programlisting>
-
- <literal>VARHDRSZ</literal> est équivalent à <literal>sizeof(int4)</literal> mais
- est considéré comme une meilleure tournure de référence à la taille de
- l'overhead pour un type à longueur variable.
- </para>
-
- <para>
- Le <xref linkend="xfunc-c-type-table"/> spécifie la correspondance entre
- les types C et les types SQL quand on écrit une fonction en langage C
- utilisant les types internes de <productname>PostgreSQL</productname>. La colonne
- <quote>Défini dans</quote> donne le fichier d'en-tête devant être inclus
- pour accéder à la définition du type (la définition effective peut se
- trouver dans un fichier différent inclus dans le fichier indiqué. Il
- est recommandé que les utilisateurs s'en tiennent à l'interface définie).
- Notez que vous devriez toujours inclure <filename>postgres.h</filename> en
- premier dans tout fichier source car il déclare un grand nombre d'éléments
- dont vous aurez besoin de toute façon.
- </para>
-
- <table tocentry="1" id="xfunc-c-type-table">
- <title>Équivalence des types C et des types SQL intégrés</title>
- <tgroup cols="3">
- <colspec colnum="1" colwidth="0.7*"/>
- <colspec colnum="2" colwidth="0.5*"/>
- <colspec colnum="3" colwidth="1.8*"/>
- <thead>
- <row>
- <entry>
- Type SQL
- </entry>
- <entry>
- Type C
- </entry>
- <entry>
- Défini dans
- </entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><type>abstime</type></entry>
- <entry><type>AbsoluteTime</type></entry>
- <entry><filename>utils/nabstime.h</filename></entry>
- </row>
- <row>
- <entry><type>boolean</type></entry>
- <entry><type>bool</type></entry>
- <entry><filename>postgres.h</filename> (intégration au
- compilateur)</entry>
- </row>
- <row>
- <entry><type>box</type></entry>
- <entry><type>BOX*</type></entry>
- <entry><filename>utils/geo_decls.h</filename></entry>
- </row>
- <row>
- <entry><type>bytea</type></entry>
- <entry><type>bytea*</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>"char"</type></entry>
- <entry><type>char</type></entry>
- <entry>(intégré au compilateur)</entry>
- </row>
- <row>
- <entry><type>character</type></entry>
- <entry><type>BpChar*</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>cid</type></entry>
- <entry><type>CommandId</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>date</type></entry>
- <entry><type>DateADT</type></entry>
- <entry><filename>utils/date.h</filename></entry>
- </row>
- <row>
- <entry><type>smallint</type> (<type>int2</type>)</entry>
- <entry><type>int2</type> or <type>int16</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>int2vector</type></entry>
- <entry><type>int2vector*</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>integer</type> (<type>int4</type>)</entry>
- <entry><type>int4</type> or <type>int32</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>real</type> (<type>float4</type>)</entry>
- <entry><type>float4*</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>double precision</type> (<type>float8</type>)</entry>
- <entry><type>float8*</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>interval</type></entry>
- <entry><type>Interval*</type></entry>
- <entry><filename>utils/timestamp.h</filename></entry>
- </row>
- <row>
- <entry><type>lseg</type></entry>
- <entry><type>LSEG*</type></entry>
- <entry><filename>utils/geo_decls.h</filename></entry>
- </row>
- <row>
- <entry><type>name</type></entry>
- <entry><type>Name</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>oid</type></entry>
- <entry><type>Oid</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>oidvector</type></entry>
- <entry><type>oidvector*</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>path</type></entry>
- <entry><type>PATH*</type></entry>
- <entry><filename>utils/geo_decls.h</filename></entry>
- </row>
- <row>
- <entry><type>point</type></entry>
- <entry><type>POINT*</type></entry>
- <entry><filename>utils/geo_decls.h</filename></entry>
- </row>
- <row>
- <entry><type>regproc</type></entry>
- <entry><type>regproc</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>reltime</type></entry>
- <entry><type>RelativeTime</type></entry>
- <entry><filename>utils/nabstime.h</filename></entry>
- </row>
- <row>
- <entry><type>text</type></entry>
- <entry><type>text*</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>tid</type></entry>
- <entry><type>ItemPointer</type></entry>
- <entry><filename>storage/itemptr.h</filename></entry>
- </row>
- <row>
- <entry><type>time</type></entry>
- <entry><type>TimeADT</type></entry>
- <entry><filename>utils/date.h</filename></entry>
- </row>
- <row>
- <entry><type>time with time zone</type></entry>
- <entry><type>TimeTzADT</type></entry>
- <entry><filename>utils/date.h</filename></entry>
- </row>
- <row>
- <entry><type>timestamp</type></entry>
- <entry><type>Timestamp*</type></entry>
- <entry><filename>utils/timestamp.h</filename></entry>
- </row>
- <row>
- <entry><type>tinterval</type></entry>
- <entry><type>TimeInterval</type></entry>
- <entry><filename>utils/nabstime.h</filename></entry>
- </row>
- <row>
- <entry><type>varchar</type></entry>
- <entry><type>VarChar*</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- <row>
- <entry><type>xid</type></entry>
- <entry><type>TransactionId</type></entry>
- <entry><filename>postgres.h</filename></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <para>
- Maintenant que nous avons passé en revue toutes les structures possibles
- pour les types de base, nous pouvons donner quelques exemples de vraies
- fonctions.
- </para>
- </sect2>
-
- <sect2>
- <title>Conventions d'appel de la version 0</title>
-
- <para>
- Nous présentons l'<quote>ancien style</quote> de convention d'appel en
- premier — bien que cette approche soit maintenant déconseillée, elle est
- plus facile à maîtriser au début. Dans la méthode version-0, les arguments
- et résultats de la fonction C sont simplement déclarés dans le style C
- normal mais en faisant attention à utiliser la représentation C de chaque
- type de données SQL comme montré ci-dessus.
- </para>
-
- <para>
- Voici quelques exemples :
-
-<programlisting><![CDATA[#include "postgres.h"
-#include <string.h>
-
-/* par valeur */
-
-int
-add_one(int arg)
-{
- return arg + 1;
-}
-
-/* par référence, taille fixe */
-
-float8 *
-add_one_float8(float8 *arg)
-{
- float8 *result = (float8 *) palloc(sizeof(float8));
-
- *result = *arg + 1.0;
-
- return result;
-}
-
-Point *
-makepoint(Point *pointx, Point *pointy)
-{
- Point *new_point = (Point *) palloc(sizeof(Point));
-
- new_point->x = pointx->x;
- new_point->y = pointy->y;
-
- return new_point;
-}
-
-/* par référence, taille variable */
-
-text *
-copytext(text *t)
-{
- /*
- * VARSIZE est la taille totale de la structure en octets.
- */
- text *new_t = (text *) palloc(VARSIZE(t));
- SET_VARSIZE(new_t, VARSIZE(t));
- /*
- * VARDATA est un pointeur sur la région de données de la structure.
- */
- memcpy((void *) VARDATA(new_t), /* destination */
- (void *) VARDATA(t), /* source */
- VARSIZE(t) - VARHDRSZ); /* nombre d'octets */
- return new_t;
-}
-
-text *
-concat_text(text *arg1, text *arg2)
-{
- int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
- text *new_text = (text *) palloc(new_text_size);
-
- SET_VARSIZE(new_text, new_text_size);
- memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
- memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ),
- VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
- return new_text;
-}
-]]></programlisting>
- </para>
-
- <para>
- En supposant que le code ci-dessus ait été écrit dans le fichier
- <filename>funcs.c</filename> et compilé en objet partagé, nous pourrions
- définir les fonctions pour <productname>PostgreSQL</productname> avec des
- commandes comme ceci :
-
-<programlisting>CREATE FUNCTION add_one(integer) RETURNS integer
- AS '<replaceable>DIRECTORY</replaceable>/funcs', 'add_one'
- LANGUAGE C STRICT;
-
--- notez la surcharge du nom de la fonction SQL "add_one"
-CREATE FUNCTION add_one(double precision) RETURNS double precision
- AS '<replaceable>DIRECTORY</replaceable>/funcs', 'add_one_float8'
- LANGUAGE C STRICT;
-
-CREATE FUNCTION makepoint(point, point) RETURNS point
- AS '<replaceable>DIRECTORY</replaceable>/funcs', 'makepoint'
- LANGUAGE C STRICT;
-
-CREATE FUNCTION copytext(text) RETURNS text
- AS '<replaceable>DIRECTORY</replaceable>/funcs', 'copytext'
- LANGUAGE C STRICT;
-
-CREATE FUNCTION concat_text(text, text) RETURNS text
- AS '<replaceable>DIRECTORY</replaceable>/funcs', 'concat_text'
- LANGUAGE C STRICT;
-</programlisting>
- </para>
-
- <para>
- Ici, <replaceable>DIRECTORY</replaceable> représente le répertoire
- contenant le fichier de la bibliothèque partagée (par exemple le répertoire
- du tutoriel de <productname>PostgreSQL</productname>, qui contient le code
- des exemples utilisés dans cette section). (Un meilleur style aurait été
- d'écrire seulement <literal>'funcs'</literal> dans la clause <literal>AS</literal>, après
- avoir ajouté <replaceable>DIRECTORY</replaceable> au chemin de recherche.
- Dans tous les cas, nous pouvons omettre l'extension spécifique au système
- pour les bibliothèques partagées, communément <literal>.so</literal> ou
- <literal>.sl</literal>.)
- </para>
-
- <para>
- Remarquez que nous avons spécifié la fonction comme <quote>STRICT</quote>,
- ce qui signifie que le système devra automatiquement supposer un résultat
- NULL si n'importe quelle valeur d'entrée est NULL. Ainsi, nous évitons
- d'avoir à vérifier l'existence d'entrées NULL dans le code de la fonction.
- Sinon, nous aurions dû contrôler explicitement les valeurs NULL en testant
- un pointeur NULL pour chaque argument passé par référence (pour les
- arguments passés par valeur, nous n'aurions même aucun moyen de contrôle !).
- </para>
-
- <para>
- Bien que cette convention d'appel soit simple à utiliser, elle n'est pas
- très portable ; sur certaines architectures, il y a des problèmes
- pour passer de cette manière des types de données plus petits
- que <type>int</type>. De plus, il n'y a pas de moyen simple de renvoyer un
- résultat NULL, ni de traiter des arguments NULL autrement qu'en rendant la
- fonction strict. La convention version-1, présentée ci-après, permet de
- surmonter ces objections.
- </para>
-
- </sect2>
-
- <sect2>
- <title>Conventions d'appel de la version 1</title>
-
- <para>
- La convention d'appel version-1 repose sur des macros pour supprimer la
- plus grande partie de la complexité du passage d'arguments et de résultats.
- La déclaration C d'une fonction en version-1 est toujours :
-
-<programlisting>Datum nom_fonction(PG_FUNCTION_ARGS)
-</programlisting>
-
- De plus, la macro d'appel :
-<programlisting>PG_FUNCTION_INFO_V1(nom_fonction);
-</programlisting>
- doit apparaître dans le même fichier source (par convention, elle est
- écrite juste avant la fonction elle-même). Cette macro n'est pas nécessaire
- pour les fonctions <literal>internal</literal> puisque <productname>PostgreSQL</productname>
- assume que toutes les fonctions internes utilisent la convention version-1.
- Elle est toutefois requise pour les fonctions chargées dynamiquement.
- </para>
-
- <para>
- Dans une fonction version-1, chaque argument existant est traité par une
- macro <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>
- correspondant au type de donnée de l'argument et le résultat est renvoyé
- par une macro
- <function>PG_RETURN_<replaceable>xxx</replaceable>()</function>
- correspondant au type renvoyé.
- <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>
- prend comme argument le nombre d'arguments de la fonction à parcourir, le
- compteur commençant à 0.
- <function>PG_RETURN_<replaceable>xxx</replaceable>()</function> prend comme
- argument la valeur effective à renvoyer.
- </para>
-
- <para>
- Voici la même fonction que précédemment, codée en style version-1
-
-<programlisting><![CDATA[#include "postgres.h"
-#include <string.h>
-#include "fmgr.h"
-
-/* par valeur */
-
-PG_FUNCTION_INFO_V1(add_one);
-
-Datum
-add_one(PG_FUNCTION_ARGS)
-{
- int32 arg = PG_GETARG_INT32(0);
-
- PG_RETURN_INT32(arg + 1);
-}
-
-/* par référence, longueur fixe */
-
-PG_FUNCTION_INFO_V1(add_one_float8);
-
-Datum
-add_one_float8(PG_FUNCTION_ARGS)
-{
- /* La macro pour FLOAT8 cache sa nature de passage par référence. */
- float8 arg = PG_GETARG_FLOAT8(0);
-
- PG_RETURN_FLOAT8(arg + 1.0);
-}
-
-PG_FUNCTION_INFO_V1(makepoint);
-
-Datum
-makepoint(PG_FUNCTION_ARGS)
-{
- /* Ici, la nature de passage par référence de Point n'est pas cachée. */
- Point *pointx = PG_GETARG_POINT_P(0);
- Point *pointy = PG_GETARG_POINT_P(1);
- Point *new_point = (Point *) palloc(sizeof(Point));
-
- new_point->x = pointx->x;
- new_point->y = pointy->y;
-
- PG_RETURN_POINT_P(new_point);
-}
-
-/* par référence, longueur variable */
-
-PG_FUNCTION_INFO_V1(copytext);
-
-Datum
-copytext(PG_FUNCTION_ARGS)
-{
- text *t = PG_GETARG_TEXT_P(0);
- /*
- * VARSIZE est la longueur totale de la structure en octets.
- */
- text *new_t = (text *) palloc(VARSIZE(t));
- SET_VARSIZE(new_t, VARSIZE(t));
- /*
- * VARDATA est un pointeur vers la région de données de la structure.
- */
- memcpy((void *) VARDATA(new_t), /* destination */
- (void *) VARDATA(t), /* source */
- VARSIZE(t) - VARHDRSZ); /* nombre d'octets */
- PG_RETURN_TEXT_P(new_t);
-}
-
-PG_FUNCTION_INFO_V1(concat_text);
-
-Datum
-concat_text(PG_FUNCTION_ARGS)
-{
- text *arg1 = PG_GETARG_TEXT_P(0);
- text *arg2 = PG_GETARG_TEXT_P(1);
- int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
- text *new_text = (text *) palloc(new_text_size);
-
- SET_VARSIZE(new_text, new_text_size);
- memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
- memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ),
- VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
- PG_RETURN_TEXT_P(new_text);
-}
-]]></programlisting>
- </para>
-
- <para>
- Les commandes <command>CREATE FUNCTION</command> sont les mêmes que pour
- leurs équivalents dans la version-0.
- </para>
-
- <para>
- Au premier coup d'œil, les conventions de codage de la version-1 peuvent
- sembler inutilement obscures. Pourtant, elles offrent nombre
- d'améliorations car les macros peuvent cacher les détails superflus. Un
- exemple est donné par la fonction <function>add_one_float8</function> où nous
- n'avons plus besoin de prêter attention au fait que le type
- <type>float8</type> est passé par référence. Un autre exemple de
- simplification est donné par les macros pour les types à longueur variable
- <literal>GETARG</literal> qui permettent un traitement plus efficace des valeurs
- <quote>toasted</quote> (compressées ou hors-ligne).
- </para>
-
- <para>
- Une des grandes améliorations dans les fonctions version-1 est le meilleur
- traitement des entrées et des résultats NULL. La macro
- <function>PG_ARGISNULL(<replaceable>n</replaceable>)</function> permet à une fonction
- de tester si chaque entrée est NULL (évidemment, ceci n'est nécessaire que
- pour les fonctions déclarées non <quote>STRICT</quote>). Comme avec les macros
- <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>, les
- arguments en entrée sont comptés à partir de zéro. Notez qu'on doit se
- garder d'exécuter
- <function>PG_GETARG_<replaceable>xxx</replaceable>()</function> jusqu'à
- ce qu'on ait vérifié que l'argument n'est pas NULL. Pour renvoyer un
- résultat NULL, exécutez la fonction
- <function>PG_RETURN_NULL()</function> ; ceci convient aussi bien dans
- les fonctions STRICT que non STRICT.
- </para>
-
- <para>
- Les autres options proposées dans l'interface de nouveau style sont deux
- variantes des macros
- <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>. La
- première d'entre elles,
- <function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>,
- garantit le renvoi d'une copie de l'argument spécifié où nous pouvons
- écrire en toute sécurité (les macros normales peuvent parfois renvoyer
- un pointeur vers une valeur physiquement mise en mémoire dans une table qui
- ne doit pas être modifiée. En utilisant les macros
- <function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>, on
- garantit l'écriture du résultat). La seconde variante se compose des macros
- <function>PG_GETARG_<replaceable>xxx</replaceable>_SLICE()</function>
- qui prennent trois arguments. Le premier est le nombre d'arguments de la
- fonction (comme ci-dessus). Le second et le troisième sont le décalage et
- la longueur du segment qui doit être renvoyé. Les décalages sont comptés à
- partir de zéro et une longueur négative demande le renvoi du reste de la
- valeur. Ces macros procurent un accès plus efficace à des parties de
- valeurs à grande dimension dans le cas où elles ont un type de stockage en
- mémoire <quote>external</quote> (le type de stockage d'une colonne peut
- être spécifié en utilisant <literal>ALTER TABLE
- <replaceable>nom_table</replaceable> ALTER COLUMN
- <replaceable>nom_colonne</replaceable> SET STORAGE
- <replaceable>typestockage</replaceable></literal>.
- <replaceable>typestockage</replaceable> est un type parmi
- <literal>plain</literal>, <literal>external</literal>, <literal>extended</literal> ou
- <literal>main</literal>).
- </para>
-
- <para>
- Enfin, les conventions d'appels de la version-1 rendent possible le renvoi
- de résultats d'ensemble (<xref linkend="xfunc-c-return-set"/>),
- l'implémentation de fonctions déclencheurs (<xref
- linkend="triggers"/>) et d'opérateurs d'appel de langage procédural (<xref
- linkend="plhandler"/>). Le code version-1 est aussi plus portable que celui
- de version-0 car il ne contrevient pas aux restrictions du protocole
- d'appel de fonction en C standard. Pour plus de détails, voir
- <filename>src/backend/utils/fmgr/README</filename> dans les fichiers
- sources de la distribution.
- </para>
-
- </sect2>
-
- <sect2>
- <title>Écriture du code</title>
-
- <para>
- Avant de nous intéresser à des sujets plus avancés, nous devons discuter de
- quelques règles de codage des fonctions en langage C de
- <productname>PostgreSQL</productname>. Bien qu'il soit possible de charger
- des fonctions écrites dans des langages autre que le C dans
- <productname>PostgreSQL</productname>, c'est habituellement difficile
- (quand c'est possible) parce que les autres langages comme C++, FORTRAN ou
- Pascal ne suivent pas fréquemment les mêmes conventions de nommage que le
- C. C'est-à-dire que les autres langages ne passent pas les arguments et ne
- renvoient pas les valeurs entre fonctions de la même manière. Pour cette
- raison, nous supposerons que nos fonctions en langage C sont réellement
- écrites en C.
- </para>
- <para>
- Les règles de base pour l'écriture de fonctions C sont les suivantes :
-
- <itemizedlist>
- <listitem>
- <para>
- Utilisez <literal>pg_config
- --includedir-server</literal><indexterm><primary>pg_config</primary><secondary>
- avec des fonctions C définies par l'utilisateur</secondary></indexterm> pour découvrir où
- sont installés les fichiers d'en-tête du serveur
- <productname>PostgreSQL</productname> sur votre système (ou sur le système de vos
- utilisateurs).
- </para>
- </listitem>
-
- <listitem>
- <para>
- Compilez et liez votre code de façon à ce qu'il soit chargé dynamiquement
- dans <productname>PostgreSQL</productname>, ce qui requiert des informations
- spéciales. Voir <xref linkend="dfunc"/> pour une explication détaillée
- sur la façon de le faire pour votre système d'exploitation spécifique.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Rappelez-vous to définir un <quote>bloc magique</quote> pour votre
- bibliothèque partagée, comme décrit dans <xref linkend="xfunc-c-dynload"/>.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Quand vous allouez de la mémoire, utilisez les fonctions
- <productname>PostgreSQL</productname>
- <function>palloc</function><indexterm><primary>palloc</primary></indexterm> et
- <function>pfree</function><indexterm><primary>pfree</primary></indexterm>
- au lieu des fonctions correspondantes <function>malloc</function> et
- <function>free</function> de la bibliothèque C. La mémoire allouée par
- <function>palloc</function> sera libérée automatiquement à la fin de
- chaque transaction, empêchant des débordements de mémoire.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Remettez toujours à zéro les octets de vos structures en utilisant
- <function>memset</function>. Sinon, il est difficile de supporter les
- index ou les jointures de découpage car vous devez retenir seulement
- les bits significatifs de votre structure de donnée pour calculer un
- découpage. Même si vous initialisez tous les champs de votre structure,
- il peut y avoir des remplissages d'alignement (trous dans la structure)
- pouvant contenir des valeurs parasites.
- </para>
- </listitem>
-
- <listitem>
- <para>
- La plupart des types internes <productname>PostgreSQL</productname>
- sont déclarés dans <filename>postgres.h</filename> alors que les
- interfaces de gestion des fonctions
- (<symbol>PG_FUNCTION_ARGS</symbol>, etc.) sont dans
- <filename>fmgr.h</filename>. Du coup, vous aurez besoin d'inclure au
- moins ces deux fichiers. Pour des raisons de portabilité, il vaut
- mieux inclure <filename>postgres.h</filename> <emphasis>en premier</emphasis>
- avant tout autre fichier d'en-tête système ou utilisateur. En incluant
- <filename>postgres.h</filename>, il incluera également
- <filename>elog.h</filename> et <filename>palloc.h</filename> pour vous.
- </para>
- </listitem>
-
- <listitem>
- <para>
- Les noms de symboles définis dans les objets ne doivent pas entrer en
- conflit entre eux ou avec les symboles définis dans les exécutables du
- serveur <productname>PostgreSQL</productname>. Vous aurez à renommer vos
- fonctions ou variables si vous recevez un message d'erreur à cet effet.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- </sect2>
-
-&dfunc;
-
-<sect2 id="xfunc-c-pgxs">
- <title>Infrastructure de construction d'extensions</title>
-
- <indexterm zone="xfunc-c-pgxs">
- <primary>pgxs</primary>
- </indexterm>
-
- <para>
- Si vous pensez distribuer vos modules d'extension
- <productname>PostgreSQL</productname>, configurer un système de construction
- portable peut être assez compliqué. Du coup, l'installation de
- <productname>PostgreSQL</productname> fournit une infrastructure de construction pour
- les extensions, appelée <acronym>PGXS</acronym>, pour que les modules
- d'extension simples puissent être construit simplement avec un serveur
- déjà installé. Notez que cette infrastructure n'a pas pour but d'être un
- ensemble de travail universel pouvant être utilisé pour construire tous
- les logiciels s'interfaçant avec <productname>PostgreSQL</productname> ; il
- automatise simplement les règles de construction communes pour les modules
- simples d'extension du serveur. Pour des paquetages plus complexes, vous
- aurez besoin d'écrire votre propre système de construction.
- </para>
-
- <para>
- Pour utiliser l'infrastructure de votre extension, vous devez écrire un
- simple fichier makefile. Dans ce fichier, vous devez configurer quelques
- variables et inclure enfin le makefile global <acronym>PGXS</acronym>.
- Voici un exemple qui construit un module d'extension nommé
- <literal>isbn_issn</literal> consistant en une bibliothèque partagée, un
- script SQL et un fichier texte de documentation :
-<programlisting>MODULES = isbn_issn
-DATA_built = isbn_issn.sql
-DOCS = README.isbn_issn
-
-PG_CONFIG := pg_config
-PGXS := $(shell $(PG_CONFIG) --pgxs)
-include $(PGXS)
-</programlisting>
- Les trois dernières lignes devraient toujours être identiques. Plus tôt
- dans le fichier, vous affectez des variables ou ajoutez vos propres règles
- pour <application>make</application>.
- </para>
-
- <para>
- Les variables suivantes peuvent être configurées :
-
- <variablelist>
- <varlistentry>
- <term><varname>MODULES</varname></term>
- <listitem>
- <para>
- liste des objets partagés à construire à partir du fichier source
- de même base (ne pas inclure le suffixe dans cette liste)
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>DATA</varname></term>
- <listitem>
- <para>
- fichiers spécifiques à installer dans
- <literal><replaceable>prefix</replaceable>/share/contrib</literal>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>DATA_built</varname></term>
- <listitem>
- <para>
- fichiers spécifiques à installer dans
- <literal><replaceable>prefix</replaceable>/share/contrib</literal>
- qui ont besoin d'être construit au début
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>DOCS</varname></term>
- <listitem>
- <para>
- fichiers spécifiques à installer dans
- <literal><replaceable>prefix</replaceable>/doc/contrib</literal>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>SCRIPTS</varname></term>
- <listitem>
- <para>
- fichiers script (pas des binaires) à installer dans
- <literal><replaceable>prefix</replaceable>/bin</literal>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>SCRIPTS_built</varname></term>
- <listitem>
- <para>
- fichiers script (pas des binaires) à installer dans
- <literal><replaceable>prefix</replaceable>/bin</literal>
- qui ont besoin d'être construit au début
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>REGRESS</varname></term>
- <listitem>
- <para>
- liste des cas de tests de regression (sans suffixe), voir ci-dessous
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- ou au moins un parmi ces deux-là :
-
- <variablelist>
- <varlistentry>
- <term><varname>PROGRAM</varname></term>
- <listitem>
- <para>
- un programme binaire à construire (liste des fichiers objets dans
- <varname>OBJS</varname>)
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>MODULE_big</varname></term>
- <listitem>
- <para>
- un objet partagé à construire (liste des fichiers objets dans
- <varname>OBJS</varname>)
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- Ce qui suit peut être configuré :
-
- <variablelist>
-
- <varlistentry>
- <term><varname>EXTRA_CLEAN</varname></term>
- <listitem>
- <para>
- fichiers supplémentaires à supprimer dans <literal>make
- clean</literal>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>PG_CPPFLAGS</varname></term>
- <listitem>
- <para>
- sera ajouté à <varname>CPPFLAGS</varname>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>PG_LIBS</varname></term>
- <listitem>
- <para>
- sera ajouté à la ligne de liens <varname>PROGRAM</varname>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>SHLIB_LINK</varname></term>
- <listitem>
- <para>
- sera ajouté à la ligne de lien <varname>MODULE_big</varname>
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><varname>PG_CONFIG</varname></term>
- <listitem>
- <para>
- chemin vers le programme <application>pg_config</application> indiquant
- l'installation de <productname>PostgreSQL</productname> qui sert à la
- construction (généralement <literal>pg_config</literal> pour utiliser
- le premier programme trouvé dans la variable d'environnement
- <varname>PATH</varname>)
- </para>
- </listitem>
- </varlistentry>
- </variablelist>
-
- </para>
-
- <para>
- Nommez ce fichier <literal>Makefile</literal> et placez le dans le répertoire
- qui contient votre extension. Ensuite, vous pouvez lancer la compilation
- avec <literal>make</literal>, et plus tard <literal>make
- install</literal> pour installer votre module. Par défaut, l'extension est
- compilée et installée pour l'installation de
- <productname>PostgreSQL</productname> qui correspond au premier programme
- <command>pg_config</command> trouvée dans votre chemin. Vous pouvez utiliser
- utiliser une autre installation en configurant <varname>PG_CONFIG</varname>
- pour qu'il pointe vers le bon <command>pg_config</command>, soit dans le
- fichier Makefile soit sur la ligne de commande de <literal>make</literal>.
- </para>
-
- <caution>
- <para>
- Modifier <varname>PG_CONFIG</varname> fonctionne seulement en construisant
- à partir d'une installation <productname>PostgreSQL</productname> 8.3 ou
- ultérieure. Les anciennes versions ne l'utilisent pas ; vous devez
- donc modifier votre variable <varname>PATH</varname> pour sélectionner
- la bonne installation.
- </para>
- </caution>
-
- <para>
- Les scripts listés dans la variable <varname>REGRESS</varname> sont utilisés
- pour les tests de régression de votre module, tout comme <literal>make
- installcheck</literal> est utilisé pour votre serveur
- <productname>PostgreSQL</productname>. Pour que ceci fonctionne, vous avez
- besoin d'avoir un sous-répertoire nommé <literal>sql/</literal> dans le
- répertoire de votre extension, à l'intérieur duquel vous placez un fichier
- pour chaque groupe de tests que vous voulez exécuter. Les fichiers doivent
- avoir l'extension <literal>.sql</literal>, qui ne seront pas inclus dans la
- liste <varname>REGRESS</varname> dans le Makefile. Pour chaque test, il doit
- exister un fichier contenant le résultat attendu dans un sous-répertoire nommé
- <literal>expected/</literal>, avec l'extension <literal>.out</literal>. Les
- tests sont exécutés via <literal>make installcheck</literal>, et le résultat sera
- comparé aux fichiers attendus. Les différences seront écrites dans le fichier
- <literal>regression.diffs</literal> dans un format <command>diff -c</command>.
- Notez qu'essayer d'exécuter un test à qui il manque le fichier sera rapporté
- comme un <quote>problème</quote>, donc assurez-vous que vous avez bien tous les
- fichiers attendus.
- </para>
-
- <tip>
- <para>
- La façon la plus simple de créer les fichiers attendus est de créer des
- fichiers vides, puis de vérifier avec précaution les fichiers résultants
- après un test d'exécution (à trouver dans un répertoire
- <literal>results/</literal>), et les copier dans
- <literal>expected/</literal> s'ils correspondent à votre souhait suite aux
- tests.
- </para>
- </tip>
- </sect2>
-
-
- <sect2>
- <title>Arguments de type composite</title>
-
- <para>
- Les types composites n'ont pas une organisation fixe comme les structures
- en C. Des instances d'un type composite peuvent contenir des champs NULL.
- De plus, les types composites faisant partie d'une hiérarchie d'héritage
- peuvent avoir des champs différents des autres membres de la même
- hiérarchie. En conséquence, <productname>PostgreSQL</productname> propose
- une interface de fonction pour accéder depuis le C aux champs des types
- composites.
- </para>
-
- <para>
- Supposons que nous voulions écrire une fonction pour répondre à la requête :
-<programlisting>SELECT nom, c_surpaye(emp, 1500) AS surpaye
- FROM emp
- WHERE nom = 'Bill' OR nom = 'Sam';
-</programlisting>
-
- En utilisant les conventions d'appel de la version 0, nous pouvons définir
- <function>c_surpaye</function> comme :
-
-<programlisting><![CDATA[#include "postgres.h"
-#include "executor/executor.h" /* pour GetAttributeByName() */
-
-bool
-c_surpaye(HeapTupleHeader *t, /* la ligne courante d'emp */
- int32 limite)
-{
- bool isNULL;
- int32 salaire;
-
- salaire = DatumGetInt32(GetAttributeByName(t, "salaire", &isNULL));
- if (isNULL)
- return false;
- return salaire > limite;
-}
-]]></programlisting>
-
- Dans le codage version-1, le code ci-dessus devient :
-
-<programlisting><![CDATA[#include "postgres.h"
-#include "executor/executor.h" /* pour GetAttributeByName() */
-
-PG_FUNCTION_INFO_V1(c_overpaid);
-
-Datum
-c_overpaid(PG_FUNCTION_ARGS)
-{
- HeapTupleHeader *t = (HeapTupleHeader *) PG_GETARG_HEAPTUPLEHEADER(0);
- int32 limite = PG_GETARG_INT32(1);
- bool isNULL;
- Datum salaire;
-
- salaire = GetAttributeByName(t, "salaire", &isNULL);
- if (isNULL)
- PG_RETURN_BOOL(false);
- /* Autrement, nous pourrions préférer de lancer PG_RETURN_NULL() pour un
- salaire NULL.
- */
-
- PG_RETURN_BOOL(DatumGetInt32(salaire) > limite);
-}
-]]></programlisting>
- </para>
-
- <para>
- <function>GetAttributeByName</function> est la fonction système
- <productname>PostgreSQL</productname> qui renvoie les attributs depuis une
- colonne spécifiée. Elle a trois arguments : l'argument de type
- <type>HeapTupleHeader</type> passé à la fonction, le nom de l'attribut
- recherché et un paramètre de retour qui indique si l'attribut est NULL.
- <function>GetAttributeByName</function> renvoie une valeur de type
- <type>Datum</type> que vous pouvez convertir dans un type voulu en
- utilisant la macro appropriée
- <function>DatumGet<replaceable>XXX</replaceable>()</function>. Notez que
- la valeur de retour est insignifiante si le commutateur NULL est
- positionné ; il faut toujours vérifier le commutateur NULL avant de commencer
- à faire quelque chose avec le résultat.
- </para>
-
- <para>
- Il y a aussi <function>GetAttributeByNum</function>, qui sélectionne
- l'attribut cible par le numéro de colonne au lieu de son nom.
- </para>
-
- <para>
- La commande suivante déclare la fonction <function>c_surpaye</function>
- en SQL :
-
-<programlisting>CREATE FUNCTION c_surpaye(emp, integer) RETURNS boolean
- AS '<replaceable>DIRECTORY</replaceable>/funcs', 'c_surpaye'
- LANGUAGE C STRICT;
-</programlisting>
-
- Notez que nous avons utilisé <literal>STRICT</literal> pour que nous n'ayons pas à
- vérifier si les arguments en entrée sont NULL.
- </para>
- </sect2>
-
- <sect2>
- <title>Renvoi de lignes (types composites)</title>
-
- <para>
- Pour renvoyer une ligne ou une valeur de type composite à partir d'une
- fonction en langage C, vous pouvez utiliser une API spéciale qui fournit
- les macros et les fonctions dissimulant en grande partie la complexité
- liée à la construction de types de données composites. Pour utiliser cette
- API, le fichier source doit inclure :
- <programlisting>#include "funcapi.h"</programlisting>
- </para>
-
- <para>
- Il existe deux façons de construire une valeur de données composites
- (autrement dit un <quote>tuple</quote>) : vous pouvez le construire à
- partir d'un tableau de valeurs Datum ou à partir d'un tableau de
- chaînes C qui peuvent passer dans les fonctions de conversion des types
- de données du tuple. Quelque soit le cas, vous avez d'abord besoin
- d'obtenir et de construire un descripteur <structname>TupleDesc</structname> pour
- la structure du tuple. En travaillant avec des Datums, vous passez le
- <structname>TupleDesc</structname> à <function>BlessTupleDesc</function>, puis vous appelez
- <function>heap_form_tuple</function> pour chaque ligne. En travaillant avec des
- chaînes C, vous passez <structname>TupleDesc</structname> à
- <function>TupleDescGetAttInMetadata</function>, puis vous appelez
- <function>BuildTupleFromCStrings</function> pour chaque ligne. Dans le cas d'une
- fonction renvoyant un ensemble de tuple, les étapes de configuration
- peuvent toutes être entreprises une fois lors du premier appel à la
- fonction.
- </para>
-
- <para>
- Plusieurs fonctions d'aide sont disponibles pour configurer le
- <structname>TupleDesc</structname> requis. La façon recommandée de le faire dans la
- plupart des fonctions renvoyant des valeurs composites est d'appeler :
-<programlisting>TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo,
- Oid *resultTypeId,
- TupleDesc *resultTupleDesc)
-</programlisting>
- en passant la même structure <literal>fcinfo</literal> que celle passée à la
- fonction appelante (ceci requiert bien sûr que vous utilisez les
- conventions d'appel version-1). <varname>resultTypeId</varname> peut être
- spécifié comme <literal>NULL</literal> ou comme l'adresse d'une variable locale
- pour recevoir l'OID du type de résultat de la fonction.
- <varname>resultTupleDesc</varname> devrait être l'adresse d'une variable
- <structname>TupleDesc</structname> locale. Vérifiez que le résultat est
- <literal>TYPEFUNC_COMPOSITE</literal> ; dans ce cas,
- <varname>resultTupleDesc</varname> a été rempli avec le
- <structname>TupleDesc</structname> requis (si ce n'est pas le cas, vous pouvez
- rapporter une erreur pour une <quote>fonction renvoyant un enregistrement
- appelé dans un contexte qui ne peut pas accepter ce type
- enregistrement</quote>).
- </para>
-
- <tip>
- <para>
- <function>get_call_result_type</function> peut résoudre le vrai type du
- résultat d'une fonction polymorphique ; donc, il est utile pour les
- fonctions qui renvoient des résultats scalaires polymorphiques, pas
- seulement les fonctions qui renvoient des types composites. Le résultat
- <varname>resultTypeId</varname> est principalement utile pour les fonctions
- renvoyant des scalaires polymorphiques.
- </para>
- </tip>
-
- <note>
- <para>
- <function>get_call_result_type</function> a une fonction cousine
- <function>get_expr_result_type</function>, qui peut être utilisée pour résoudre
- le tupe attendu en sortie en un appel de fonction représenté par
- un arbre d'expressions. Ceci peut être utilisé pour tenter de déterminer
- le type de résultat sans entrer dans la fonction elle-même. Il existe
- aussi <function>get_func_result_type</function>, qui peut seulement être utilisée
- quand l'OID de la fonction est disponible. Néanmoins, ces fonctions ne
- sont pas capables de gérer les fonctions déclarées renvoyer des
- enregistrements (<structname>record</structname>).
- <function>get_func_result_type</function> ne peut pas résoudre les types
- polymorphiques, donc vous devriez utiliser de préférence
- <function>get_call_result_type</function>.
- </para>
- </note>
-
- <para>
- Les fonctions anciennes, et maintenant obsolètes, qui permettent d'obtenir des
- <structname>TupleDesc</structname> sont :
-<programlisting>TupleDesc RelationNameGetTupleDesc(const char *relname)
-</programlisting>
- pour obtenir un <structname>TupleDesc</structname> pour le type de ligne d'une
- relation nommée ou :
-<programlisting>TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
-</programlisting>
- pour obtenir une <structname>TupleDesc</structname> basée sur l'OID d'un type. Ceci
- peut être utilisé pour obtenir un <structname>TupleDesc</structname> soit pour un
- type de base, soit pour un type composite. Néanmoins, cela ne fonctionnera
- pas pour une fonction qui renvoie <structname>record</structname> et cela ne résoudra
- pas les types polymorphiques.
- </para>
-
- <para>
- Une fois que vous avez un <structname>TupleDesc</structname>, appelez :
-<programlisting>TupleDesc BlessTupleDesc(TupleDesc tupdesc)
-</programlisting>
- si vous pensez travailler avec des Datums ou :
-<programlisting>AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
-</programlisting>
- si vous pensez travailler avec des chaînes C. Si vous écrivez une
- fonction renvoyant un ensemble, vous pouvez sauvegarder les résultats
- de ces fonctions dans la structure dans le
- <structname>FuncCallContext</structname> — utilisez le champ
- <structfield>tuple_desc</structfield> ou <structfield>attinmeta</structfield> respectivement.
- </para>
-
- <para>
- Lorsque vous fonctionnez avec des Datums, utilisez :
-<programlisting>HeapTuple heap_form_tuple(TupleDesc tupdesc, Datum *values, bool *isnull)
-</programlisting>
- pour construire une donnée utilisateur <structname>HeapTuple</structname> indiquée
- dans le format Datum.
- </para>
-
- <para>
- Lorsque vous travaillez avec des chaînes C, utilisez :
-<programlisting>HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
-</programlisting>
- pour construire une donnée utilisateur <structname>HeapTuple</structname> indiquée
- dans le format des chaînes C. <literal>values</literal> est un tableau de
- chaîne C, une pour chaque attribut de la ligne renvoyée. Chaque chaîne
- C doit être de la forme attendue par la fonction d'entrée du type de
- donnée de l'attribut. Afin de renvoyer une valeur NULL pour un des
- attributs, le pointeur correspondant dans le tableau de valeurs
- (<parameter>values</parameter>) doit être fixé à <symbol>NULL</symbol>. Cette fonction
- demandera à être appelée pour chaque ligne que vous renvoyez.
- </para>
-
- <para>
- Une fois que vous avez construit un tuple devant être renvoyé par votre
- fonction, vous devez le convertir en type <type>Datum</type>. Utilisez :
-<programlisting>HeapTupleGetDatum(HeapTuple tuple)
-</programlisting>
- pour convertir un type <type>HeapTuple</type> en un Datum valide.
- Ce <type>Datum</type> peut être renvoyé directement si vous envisagez de
- renvoyer juste une simple ligne ou bien il peut être utilisé pour renvoyer
- la valeur courante dans une fonction renvoyant un ensemble.
- </para>
-
- <para>
- Un exemple figure dans la section suivante.
- </para>
-
- </sect2>
-
- <sect2 id="xfunc-c-return-set">
- <title>Renvoi d'ensembles</title>
-
- <para>
- Il existe aussi une API spéciale procurant le moyen de renvoyer des
- ensembles (lignes multiples) depuis une fonction en langage C. Une fonction
- renvoyant un ensemble doit suivre les conventions d'appel de la version-1.
- Aussi, les fichiers source doivent inclure l'en-tête
- <filename>funcapi.h</filename>, comme ci-dessus.
- </para>
-
- <para>
- Une fonction renvoyant un ensemble (<acronym>SRF</acronym> : <quote>set
- returning function</quote>) est appelée une fois pour chaque élément
- qu'elle renvoie. La <acronym>SRF</acronym> doit donc sauvegarder suffisamment
- l'état pour se rappeler ce qu'elle était en train de faire et renvoyer le
- prochain élément à chaque appel. La structure
- <structname>FuncCallContext</structname> est offerte pour assister le contrôle de ce
- processus. À l'intérieur d'une fonction,
- <literal>fcinfo->flinfo->fn_extra</literal> est utilisée pour conserver un
- pointeur vers <structname>FuncCallContext</structname> au cours des appels
- successifs.
-
-<programlisting>typedef struct
-{
- /*
- * Number of times we've been called before
- *
- * call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
- * incremented for you every time SRF_RETURN_NEXT() is called.
- */
- uint32 call_cntr;
-
- /*
- * OPTIONAL maximum number of calls
- *
- * max_calls is here for convenience only and setting it is optional.
- * If not set, you must provide alternative means to know when the
- * function is done.
- */
- uint32 max_calls;
-
- /*
- * OPTIONAL pointer to result slot
- *
- * This is obsolete and only present for backwards compatibility, viz,
- * user-defined SRFs that use the deprecated TupleDescGetSlot().
- */
- TupleTableSlot *slot;
-
- /*
- * OPTIONAL pointer to miscellaneous user-provided context information
- *
- * user_fctx is for use as a pointer to your own data to retain
- * arbitrary context information between calls of your function.
- */
- void *user_fctx;
-
- /*
- * OPTIONAL pointer to struct containing attribute type input metadata
- *
- * attinmeta is for use when returning tuples (i.e., composite data types)
- * and is not used when returning base data types. It is only needed
- * if you intend to use BuildTupleFromCStrings() to create the return
- * tuple.
- */
- AttInMetadata *attinmeta;
-
- /*
- * memory context used for structures that must live for multiple calls
- *
- * multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used
- * by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory
- * context for any memory that is to be reused across multiple calls
- * of the SRF.
- */
- MemoryContext multi_call_memory_ctx;
-
- /*
- * OPTIONAL pointer to struct containing tuple description
- *
- * tuple_desc is for use when returning tuples (i.e. composite data types)
- * and is only needed if you are going to build the tuples with
- * heap_form_tuple() rather than with BuildTupleFromCStrings(). Note that
- * the TupleDesc pointer stored here should usually have been run through
- * BlessTupleDesc() first.
- */
- TupleDesc tuple_desc;
-
-} FuncCallContext;
-</programlisting>
- </para>
-
- <para>
- Une <acronym>SRF</acronym> utilise plusieurs fonctions et macros qui manipulent
- automatiquement la structure <structname>FuncCallContext</structname> (et s'attendent
- à la trouver via <literal>fn_extra</literal>). Utilisez :
-<programlisting>SRF_IS_FIRSTCALL()
-</programlisting>
- pour déterminer si votre fonction est appelée pour la première fois. Au
- premier appel, utilisez :
-<programlisting>SRF_FIRSTCALL_INIT()
-</programlisting>
- pour initialiser la structure <structname>FuncCallContext</structname>. À chaque
- appel de fonction, y compris le premier, utilisez :
-<programlisting>SRF_PERCALL_SETUP()
-</programlisting>
- pour une mise à jour correcte en vue de l'utilisation de
- <structname>FuncCallContext</structname> et pour nettoyer toutes les données
- renvoyées précédemment et conservées depuis le dernier passage de la
- fonction.
- </para>
-
- <para>
- Si votre fonction a des données à renvoyer, utilisez :
-<programlisting>SRF_RETURN_NEXT(funcctx, result)
-</programlisting>
- pour les renvoyer à l'appelant. (<literal>result</literal> doit être de type
- <type>Datum</type>, soit une valeur simple, soit un tuple préparé comme décrit
- ci-dessus.) Enfin, quand votre fonction a fini de renvoyer des données,
- utilisez :
-<programlisting>SRF_RETURN_DONE(funcctx)
-</programlisting>
- pour nettoyer et terminer la <acronym>SRF</acronym>.
- </para>
-
- <para>
- Lors de l'appel de la <acronym>SRF</acronym>, le contexte mémoire courant est un
- contexte transitoire qui est effacé entre les appels. Cela signifie que
- vous n'avez pas besoin d'appeler <function>pfree</function> sur tout ce que vous
- avez alloué en utilisant <function>palloc</function> ; ce sera supprimé de
- toute façon. Toutefois, si vous voulez allouer des structures de données
- devant persister tout au long des appels, vous avez besoin de les conserver
- quelque part. Le contexte mémoire référencé par
- <structfield>multi_call_memory_ctx</structfield> est un endroit approprié pour toute
- donnée devant survivre jusqu'à l'achèvement de la fonction <acronym>SRF</acronym>.
- Dans la plupart des cas, cela signifie que vous devrez basculer vers
- <structfield>multi_call_memory_ctx</structfield> au moment de la préparation du
- premier appel.
- </para>
- <para>
- Voici un exemple complet de pseudo-code :
-<programlisting>Datum
-my_set_returning_function(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- Datum result;
- MemoryContext oldcontext;
- <replaceable>further declarations as needed</replaceable>
-
- if (SRF_IS_FIRSTCALL())
- {
- funcctx = SRF_FIRSTCALL_INIT();
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
- /* One-time setup code appears here: */
- <replaceable>user code</replaceable>
- <replaceable>if returning composite</replaceable>
- <replaceable>build TupleDesc, and perhaps AttInMetadata</replaceable>
- <replaceable>endif returning composite</replaceable>
- <replaceable>user code</replaceable>
- MemoryContextSwitchTo(oldcontext);
- }
-
- /* Each-time setup code appears here: */
- <replaceable>user code</replaceable>
- funcctx = SRF_PERCALL_SETUP();
- <replaceable>user code</replaceable>
-
- /* this is just one way we might test whether we are done: */
- if (funcctx->call_cntr < funcctx->max_calls)
- {
- /* Here we want to return another item: */
- <replaceable>user code</replaceable>
- <replaceable>obtain result Datum</replaceable>
- SRF_RETURN_NEXT(funcctx, result);
- }
- else
- {
- /* Here we are done returning items and just need to clean up: */
- <replaceable>user code</replaceable>
- SRF_RETURN_DONE(funcctx);
- }
-}
-</programlisting>
- </para>
-
- <para>
- Et voici un exemple complet d'une simple <acronym>SRF</acronym> retournant un
- type composite :
- <programlisting><![CDATA[PG_FUNCTION_INFO_V1(retcomposite);
-
-Datum
-retcomposite(PG_FUNCTION_ARGS)
-{
- FuncCallContext *funcctx;
- int call_cntr;
- int max_calls;
- TupleDesc tupdesc;
- AttInMetadata *attinmeta;
-
- /* stuff done only on the first call of the function */
- if (SRF_IS_FIRSTCALL())
- {
- MemoryContext oldcontext;
-
- /* create a function context for cross-call persistence */
- funcctx = SRF_FIRSTCALL_INIT();
-
- /* switch to memory context appropriate for multiple function calls */
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
- /* total number of tuples to be returned */
- funcctx->max_calls = PG_GETARG_UINT32(0);
-
- /* Build a tuple descriptor for our result type */
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("function returning record called in context "
- "that cannot accept type record")));
-
- /*
- * generate attribute metadata needed later to produce tuples from raw
- * C strings
- */
- attinmeta = TupleDescGetAttInMetadata(tupdesc);
- funcctx->attinmeta = attinmeta;
-
- MemoryContextSwitchTo(oldcontext);
- }
-
- /* stuff done on every call of the function */
- funcctx = SRF_PERCALL_SETUP();
-
- call_cntr = funcctx->call_cntr;
- max_calls = funcctx->max_calls;
- attinmeta = funcctx->attinmeta;
-
- if (call_cntr < max_calls) /* do when there is more left to send */
- {
- char **values;
- HeapTuple tuple;
- Datum result;
-
- /*
- * Prepare a values array for building the returned tuple.
- * This should be an array of C strings which will
- * be processed later by the type input functions.
- */
- values = (char **) palloc(3 * sizeof(char *));
- values[0] = (char *) palloc(16 * sizeof(char));
- values[1] = (char *) palloc(16 * sizeof(char));
- values[2] = (char *) palloc(16 * sizeof(char));
-
- snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
- snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
- snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));
-
- /* build a tuple */
- tuple = BuildTupleFromCStrings(attinmeta, values);
-
- /* make the tuple into a datum */
- result = HeapTupleGetDatum(tuple);
-
- /* clean up (this is not really necessary) */
- pfree(values[0]);
- pfree(values[1]);
- pfree(values[2]);
- pfree(values);
-
- SRF_RETURN_NEXT(funcctx, result);
- }
- else /* do when there is no more left */
- {
- SRF_RETURN_DONE(funcctx);
- }
-}
-]]></programlisting>
-
- Voici une façon de déclarer cette fonction en SQL :
-
-<programlisting>CREATE TYPE __retcomposite AS (f1 integer, f2 integer, f3 integer);
-
-CREATE OR REPLACE FUNCTION retcomposite(integer, integer)
- RETURNS SETOF __retcomposite
- AS '<replaceable>filename</replaceable>', 'retcomposite'
- LANGUAGE C IMMUTABLE STRICT;
-</programlisting>
- Une façon différente de le faire est d'utiliser des paramètres OUT :
-<programlisting>CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
- OUT f1 integer, OUT f2 integer, OUT f3 integer)
- RETURNS SETOF record
- AS '<replaceable>filename</replaceable>', 'retcomposite'
- LANGUAGE C IMMUTABLE STRICT;
-</programlisting>
- Notez que dans cette méthode le type en sortie de la fonction est du type
- <structname>record</structname> anonyme.
- </para>
-
- <para>
- Le répertoire <filename>contrib/tablefunc</filename> situé dans les fichiers source
- de la distribution contient d'autres exemples de fonctions renvoyant des
- ensembles.</para>
- </sect2>
-
- <sect2>
- <title>Arguments polymorphes et types renvoyés</title>
-
- <para>
- Les fonctions en langage C peuvent être déclarées pour accepter et renvoyer
- les types <quote>polymorphes</quote> <type>anyelement</type>,
- <type>anyarray</type>, <type>anynonarray</type> et <type>anyenum</type>. Voir la <xref linkend="extend-types-polymorphic"/> pour une
- explication plus détaillée des fonctions polymorphes. Si les types des
- arguments ou du renvoi de la fonction sont définis comme polymorphes,
- l'auteur de la fonction ne peut pas savoir à l'avance quel type de données
- sera appelé ou bien quel type doit être renvoyé. Il y a deux routines
- offertes par <filename>fmgr.h</filename> qui permettent à une fonction en
- version-1 de découvrir les types de données effectifs de ses arguments et
- le type qu'elle doit renvoyer. Ces routines s'appellent
- <literal>get_fn_expr_rettype(FmgrInfo *flinfo)</literal> et
- <literal>get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)</literal>. Elles
- renvoient l'OID du type du résultat ou de l'argument ou InvalidOID si
- l'information n'est pas disponible. L'accès à la structure
- <literal>flinfo</literal> se fait normalement avec
- <literal>fcinfo->flinfo</literal>. Le paramètre <literal>argnum</literal> est basé à
- partir de zéro. <function>get_call_result_type</function> peut aussi être utilisé
- comme alternative à <function>get_fn_expr_rettype</function>.
- </para>
-
- <para>
- Par exemple, supposons que nous voulions écrire une fonction qui accepte un
- argument de n'importe quel type et qui renvoie un tableau uni-dimensionnel
- de ce type :
-
-<programlisting>PG_FUNCTION_INFO_V1(make_array);
-Datum
-make_array(PG_FUNCTION_ARGS)
-{
- ArrayType *result;
- Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
- Datum element;
- bool isnull;
- int16 typlen;
- bool typbyval;
- char typalign;
- int ndims;
- int dims[MAXDIM];
- int lbs[MAXDIM];
-
- if (!OidIsValid(element_type))
- elog(ERROR, "could not determine data type of input");
-
- /* get the provided element, being careful in case it's NULL */
- isnull = PG_ARGISNULL(0);
- if (isnull)
- element = (Datum) 0;
- else
- element = PG_GETARG_DATUM(0);
-
- /* we have one dimension */
- ndims = 1;
- /* and one element */
- dims[0] = 1;
- /* and lower bound is 1 */
- lbs[0] = 1;
-
- /* get required info about the element type */
- get_typlenbyvalalign(element_type, &typlen, &typbyval,
-&typalign);
-
- /* now build the array */
- result = construct_md_array(&element, &isnull, ndims, dims, lbs,
- element_type, typlen, typbyval, typalign);
-
- PG_RETURN_ARRAYTYPE_P(result);
-}
-</programlisting>
- </para>
-
- <para>
- La commande suivante déclare la fonction <function>make_array</function> en
- SQL :
-
-<programlisting>CREATE FUNCTION make_array(anyelement)
- RETURNS anyarray
- AS '<replaceable>DIRECTORY</replaceable>/funcs', 'make_array'
- LANGUAGE 'C' IMMUTABLE;
-</programlisting>
-
- Notez l'utilisation de STRICT ; ceci est primordial car le code ne se
- préoccupe pas de tester une entrée NULL.
- </para>
-
- <para>
- Il existe une variante du polymorphisme qui est seulement disponible pour
- les fonctions en langage C : elles peuvent être déclarées prendre des
- paramètres de type <literal>"any"</literal>. (Notez que ce nom de type doit
- être placé entre des guillemets doubles car il s'agit d'un mot SQL réservé.)
- Ceci fonctionne comme <type>anyelement</type> sauf qu'il ne contraint pas
- les différents arguments <literal>"any"</literal> à être du même type, pas
- plus qu'ils n'aident à déterminer le type de résultat de la fonction. Une
- fonction en langage C peut aussi déclarer son paramètre final ainsi :
- <literal>VARIADIC "any"</literal>. Cela correspondra à un ou plusieurs
- arguments réels de tout type (pas nécessairement le même type). Ces
- arguments ne seront <emphasis>pas</emphasis> placés dans un tableau
- comme c'est le cas pour les fonctions variadic normales ; ils seront
- passés séparément à la fonction. La macro <function>PG_NARGS()</function>
- et les méthodes décrites ci-dessus doivent être utilisées pour déterminer
- le nombre d'arguments réels et leur type lors de l'utilisation de cette
- fonctionnalité.
- </para>
- </sect2>
-
- <sect2>
- <title>Mémoire partagée et LWLocks</title>
-
- <para>
- Les modules peuvent réserver des LWLocks et allouer de la mémoire partagée
- au lancement du serveur. La bibliothèque partagée du module doit être
- préchargée en l'ajoutant <xref
- linkend="guc-shared-preload-libraries"/><indexterm><primary>shared-preload-libraries</primary></indexterm>.
- La mémoire partagée est réservée en appelant :
-<programlisting>void RequestAddinShmemSpace(int size)
-</programlisting>
- à partir de votre fonction <function>_PG_init</function>.
- </para>
- <para>
- Les LWLocks sont réservés en appelant :
-<programlisting>
-void RequestAddinLWLocks(int n)
-</programlisting>
- à partir de <function>_PG_init</function>.
- </para>
- <para>
- Pour éviter des cas rares possibles, chaque moteur devrait utiliser la
- fonction <function>AddinShmemInitLock</function> lors de la connexion et
- de l'initialisation de la mémoire partagée, comme indiquée ci-dessous :
-<programlisting>
- static mystruct *ptr = NULL;
-
- if (!ptr)
- {
- bool found;
-
- LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
- ptr = ShmemInitStruct("my struct name", size, &found);
- if (!ptr)
- elog(ERROR, "out of shared memory");
- if (!found)
- {
- initialize contents of shmem area;
- acquire any requested LWLocks using:
- ptr->mylockid = LWLockAssign();
- }
- LWLockRelease(AddinShmemInitLock);
- }
-</programlisting>
- </para>
- </sect2>
-
- </sect1>
Copied: traduc/tags/tv840rc1/xfunc.xml (from rev 1350, traduc/trunk/postgresql/xfunc.xml)
===================================================================
--- traduc/tags/tv840rc1/xfunc.xml (rev 0)
+++ traduc/tags/tv840rc1/xfunc.xml 2009-06-23 16:25:46 UTC (rev 1351)
@@ -0,0 +1,3268 @@
+<?xml version="1.0" encoding="ISO-8859-15"?>
+<!-- Dernière modification
+ le $Date$
+ par $Author$
+ révision $Revision$ -->
+
+ <sect1 id="xfunc">
+ <title>Fonctions utilisateur</title>
+
+ <indexterm zone="xfunc">
+ <primary>fonction</primary>
+ <secondary>utilisateur</secondary>
+ </indexterm>
+
+ <para>
+ <productname>PostgreSQL</productname> propose quatre types de
+ fonctions :
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ fonctions en langage de requête (fonctions écrites en <acronym>SQL</acronym>,
+ <xref linkend="xfunc-sql"/>)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ fonctions en langage procédural (fonctions écrites, par exemple, en
+ <application>PL/pgSQL</application> ou <application>PL/Tcl</application>,
+ <xref linkend="xfunc-pl"/>)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ fonctions internes (<xref linkend="xfunc-internal"/>)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ fonctions en langage C (<xref linkend="xfunc-c"/>)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <para>
+ Chaque type de fonction peut accepter comme arguments (paramètres) des types
+ de base, des types composites ou une combinaison de ceux-ci. De plus, chaque
+ sorte de fonction peut renvoyer un type de base ou un type composite. Les
+ fonctions pourraient aussi être définies pour renvoyer des ensembles de valeurs
+ de base ou de valeurs composites.
+ </para>
+
+ <para>
+ De nombreuses sortes de fonctions peuvent accepter ou renvoyer certains
+ pseudo-types (comme les types polymorphes) mais avec des fonctionnalités
+ variées.
+ Consultez la description de chaque type de fonction pour plus de détails.
+ </para>
+
+ <para>
+ Il est plus facile de définir des fonctions <acronym>SQL</acronym> aussi
+ allons-nous commencer par celles-ci. La plupart des concepts présentés pour
+ les fonctions <acronym>SQL</acronym> seront aussi gérés par les autres types
+ de fonctions.
+ </para>
+
+ <para>
+ Lors de la lecture de ce chapitre, il peut être utile de consulter la page
+ de référence de la commande <xref linkend="sql-createfunction"
+ endterm="sql-createfunction-title"/> pour mieux
+ comprendre les exemples. Quelques exemples extraits de ce chapitre peuvent
+ être trouvés dans les fichiers <filename>funcs.sql</filename> et
+ <filename>funcs.c</filename> du répertoire du tutoriel de la distribution
+ source de <productname>PostgreSQL</productname>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="xfunc-sql">
+ <title>Fonctions en langage de requêtes (<acronym>SQL</acronym>)</title>
+
+ <indexterm zone="xfunc-sql">
+ <primary>fonction</primary>
+ <secondary>définie par l'utilisateur</secondary>
+ <tertiary>en SQL</tertiary>
+ </indexterm>
+
+ <para>
+ Les fonctions SQL exécutent une liste arbitraire d'instructions SQL et
+ renvoient le résultat de la dernière requête de cette liste. Dans le cas
+ d'un résultat simple (pas d'ensemble), la première ligne du résultat de la
+ dernière requête sera renvoyée (gardez à l'esprit que <quote>la première
+ ligne</quote> d'un résultat multiligne n'est pas bien définie à moins
+ d'utiliser <literal>ORDER BY</literal>). Si la dernière requête de la liste ne
+ renvoie aucune ligne, la valeur NULL est renvoyée.
+ </para>
+
+ <para>
+ Une fonction SQL peut être déclarée de façon à renvoyer un ensemble (set)
+ en spécifiant le type renvoyé par la fonction comme <literal>SETOF
+ <replaceable>un_type</replaceable></literal>, ou de façon équivalente en
+ la déclarant comme <literal>RETURNS
+ TABLE(<replaceable>colonnes</replaceable>)</literal>. Dans ce
+ cas, toutes les lignes de la dernière requête sont renvoyées. Des détails
+ supplémentaires sont donnés plus loin dans ce chapitre.
+ </para>
+
+ <para>
+ Le corps d'une fonction SQL doit être constitué d'une liste d'une ou
+ de plusieurs instructions SQL séparées par des points-virgule. Un
+ point-virgule après la dernière instruction est optionnel. Sauf si la
+ fonction déclare renvoyer <type>void</type>, la dernière instruction doit
+ être un <command>SELECT</command> ou un <command>INSERT</command>,
+ <command>UPDATE</command> ou un <command>DELETE</command> qui a une clause
+ <literal>RETURNING</literal>.
+ </para>
+
+ <para>
+ Toute collection de commandes dans le langage <acronym>SQL</acronym> peut
+ être assemblée et définie comme une fonction. En plus des requêtes
+ <command>SELECT</command>, les commandes peuvent inclure des requêtes de
+ modification des données (<command>INSERT</command>,
+ <command>UPDATE</command> et <command>DELETE</command>) ainsi que
+ d'autres commandes SQL (la seule exception est que vous ne pouvez pas
+ placer de commandes <command>BEGIN</command>, <command>COMMIT</command>,
+ <command>ROLLBACK</command> ou <command>SAVEPOINT</command> dans une fonction
+ <acronym>SQL</acronym>). Néanmoins, la commande finale doit être un
+ <command>SELECT</command> ou doit avoir une clause <literal>RETURNING</literal>
+ qui renvoie ce qui a été spécifié comme type
+ de retour de la fonction. Autrement, si vous voulez définir une fonction
+ SQL qui réalise des actions mais n'a pas de valeur utile à renvoyer,
+ vous pouvez la définir comme renvoyant <type>void</type>. Par exemple, cette fonction supprime les
+ lignes avec des salaires négatifs depuis la table <literal>emp</literal> :
+
+<screen>CREATE FUNCTION nettoie_emp() RETURNS void AS '
+ DELETE FROM emp WHERE salaire < 0;
+' LANGUAGE SQL;
+
+SELECT nettoie_emp();
+
+ nettoie_emp
+ -----------
+
+ (1 row)
+</screen>
+ </para>
+
+ <para>
+ La syntaxe de la commande <command>CREATE FUNCTION</command> requiert que
+ le corps de la fonction soit écrit comme une constante de type chaîne.
+ Il est habituellement plus agréable d'utiliser les guillemets dollar
+ (voir la <xref linkend="sql-syntax-dollar-quoting"/>) pour cette constante.
+ Si vous choisissez d'utiliser la syntaxe habituelle avec des guillemets
+ simples, vous devez doubler les marques de guillemet simple
+ (<literal>'</literal>) et les antislashs (<literal>\</literal>), en supposant
+ que vous utilisez la syntaxe d'échappement de chaînes, utilisés dans le corps
+ de la fonction (voir la <xref linkend="sql-syntax-strings"/>).
+ </para>
+
+ <para>
+ Les arguments de la fonction SQL sont référencés dans le corps de la
+ fonction en utilisant la syntaxe suivante.
+ <literal>$<replaceable>n</replaceable></literal>:<literal>$1</literal> se réfère au premier argument,
+ <literal>$2</literal> au second et ainsi de suite. Si un argument est de type
+ composite, on utilisera la notation par point, par exemple
+ <literal>$1.name</literal>, pour accéder aux attributs de l'argument.
+ Les arguments peuvent seulement être utilisés comme valeurs des données, pas
+ comme des identifieurs. Du coup, par exemple, ceci est correct :
+<programlisting>INSERT INTO matable VALUES ($1);
+</programlisting>
+ mais ceci ne fonctionnera pas :
+<programlisting>INSERT INTO $1 VALUES (42);
+</programlisting>
+ </para>
+
+ <sect2 id="xfunc-sql-base-functions">
+ <title>Fonctions <acronym>SQL</acronym> sur les types de base</title>
+
+ <para>
+ La fonction <acronym>SQL</acronym> la plus simple possible n'a pas
+ d'argument et retourne un type de base tel que <type>integer</type> :
+<screen>CREATE FUNCTION un() RETURNS integer AS $$
+ SELECT 1 AS resultat;
+$$ LANGUAGE SQL;
+
+-- Autre syntaxe pour les chaînes littérales :
+CREATE FUNCTION un() RETURNS integer AS '
+ SELECT 1 AS resultat;
+' LANGUAGE SQL;
+
+SELECT un();
+
+ un
+----
+ 1
+</screen>
+ </para>
+
+ <para>
+ Notez que nous avons défini un alias de colonne avec le nom
+ <literal>resultat</literal> dans le corps de la fonction pour se référer au
+ résultat de la fonction mais cet alias n'est pas visible hors de la
+ fonction. En effet, le résultat est nommé <literal>un</literal> au lieu de
+ <literal>resultat</literal>.
+ </para>
+
+ <para>
+ Il est presque aussi facile de définir des fonctions SQL acceptant des types
+ de base comme arguments. Dans l'exemple suivant, remarquez comment nous
+ faisons référence aux arguments dans le corps de la fonction avec
+ <literal>$1</literal> et <literal>$2</literal>.
+
+<screen>CREATE FUNCTION ajoute(integer, integer) RETURNS integer AS $$
+ SELECT $1 + $2;
+$$ LANGUAGE SQL;
+
+SELECT ajoute(1, 2) AS reponse;
+
+ reponse
+---------
+ 3
+</screen>
+ </para>
+
+ <para>
+ Voici une fonction plus utile, qui pourrait être utilisée pour débiter un
+ compte bancaire :
+
+<programlisting>CREATE FUNCTION tf1 (integer, numeric) RETURNS integer AS $$
+ UPDATE banque
+ SET balance = balance - $2
+ WHERE no_compte = $1;
+ SELECT 1;
+$$ LANGUAGE SQL;
+</programlisting>
+
+ Un utilisateur pourrait exécuter cette fonction pour débiter le compte 17 de
+ 100 000 euros ainsi :
+<programlisting>SELECT tf1(17, 100.000);</programlisting>
+ </para>
+
+ <para>
+ Dans la pratique, on préférera vraisemblablement un résultat plus utile que
+ la constante 1. Une définition plus probable est :
+
+<programlisting>CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$
+ UPDATE banque
+ SET balance = balance - $2
+ WHERE no_compte = $1;
+ SELECT balance FROM banque WHERE no_compte = $1;
+$$ LANGUAGE SQL;
+</programlisting>
+
+ qui ajuste le solde et renvoie sa nouvelle valeur.
+ La même chose peut se faire en une commande en utilisant la clause
+ <literal>RETURNING</literal> :
+
+<programlisting>
+CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$
+ UPDATE banque
+ SET balance = balance - $2
+ WHERE no_compte = $1
+ RETURNING balance;
+$$ LANGUAGE SQL;
+</programlisting>
+ </para>
+
+ </sect2>
+
+ <sect2>
+ <title>Fonctions <acronym>SQL</acronym> sur les types composites</title>
+
+ <para>
+ Quand nous écrivons une fonction avec des arguments de type composite,
+ nous devons non seulement spécifier l'argument utilisé (comme nous
+ l'avons fait précédemment avec <literal>$1</literal> et <literal>$2</literal>),
+ mais aussi spécifier l'attribut désiré de cet argument (champ). Par
+ exemple, supposons que
+ <type>emp</type> soit le nom d'une table contenant des données sur les
+ employés et donc également le nom du type composite correspondant à chaque
+ ligne de la table. Voici une fonction <function>double_salaire</function>
+ qui calcule ce que serait le salaire de quelqu'un s'il était doublé :
+
+<screen>CREATE TABLE emp (
+ nom text,
+ salaire numeric,
+ age integer,
+ cubicle point
+);
+
+CREATE FUNCTION double_salaire(emp) RETURNS numeric AS $$
+ SELECT $1.salaire * 2 AS salaire;
+$$ LANGUAGE SQL;
+
+SELECT nom, double_salaire(emp.*) AS reve
+ FROM emp
+ WHERE emp.cubicle ~= point '(2,1)';
+
+ name | reve
+------+-------
+ Bill | 8400
+</screen>
+ </para>
+
+ <para>
+ Notez l'utilisation de la syntaxe <literal>$1.salaire</literal> pour
+ sélectionner un champ dans la valeur de la ligne argument. Notez également
+ comment la commande <command>SELECT</command> utilise <literal>*</literal> pour
+ sélectionner la ligne courante entière de la table comme une valeur composite
+ (<literal>emp</literal>). La ligne de la table peut aussi être référencée en
+ utilisant seulement le nom de la table ainsi :
+<screen>SELECT nom, double_salaire(emp) AS reve
+ FROM emp
+ WHERE emp.cubicle ~= point '(2,1)';
+</screen>
+ mais cette utilisation est obsolète car elle est facilement obscure.
+ </para>
+
+ <para>
+ Quelque fois, il est pratique de construire une valeur d'argument
+ composite en direct. Ceci peut se faire avec la construction
+ <literal>ROW</literal>. Par exemple, nous pouvons ajuster les données passées
+ à la fonction :
+<screen>SELECT nom, double_salaire(ROW(nom, salaire*1.1, age, cubicle)) AS reve
+ FROM emp;
+</screen>
+ </para>
+
+ <para>
+ Il est aussi possible de construire une fonction qui renvoie un type
+ composite. Voici un exemple de fonction renvoyant une seule ligne de type
+ <type>emp</type> :
+
+<programlisting>CREATE FUNCTION nouvel_emp() RETURNS emp AS $$
+ SELECT text 'Aucun' AS nom,
+ 1000.0 AS salaire,
+ 25 AS age,
+ point '(2,2)' AS cubicle;
+$$ LANGUAGE SQL;
+</programlisting>
+
+ Dans cet exemple, nous avons spécifié chacun des attributs avec une valeur
+ constante, mais un quelconque calcul aurait pu être substitué à ces
+ valeurs.
+ </para>
+
+ <para>
+ Notez deux aspects importants à propos de la définition de fonction :
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ L'ordre de la liste du SELECT doit être exactement le même que celui
+ dans lequel les colonnes apparaissent dans la table associée au type
+ composite (donner des noms aux colonnes dans le corps de la fonction,
+ comme nous l'avons
+ fait dans l'exemple, n'a aucune interaction avec le système).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Vous devez transtyper les expressions pour concorder avec la définition
+ du type composite ou bien vous aurez l'erreur suivante :
+<screen><computeroutput>ERROR: function declared to return emp returns varchar instead of text at column 1</computeroutput></screen>
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <para>
+ Un autre façon de définir la même fonction est :
+
+ <programlisting>CREATE FUNCTION nouveau_emp() RETURNS emp AS $$
+ SELECT ROW('Aucun', 1000.0, 25, '(2,2)')::emp;
+$$ LANGUAGE SQL;</programlisting>
+
+ Ici, nous écrivons un <command>SELECT</command> qui renvoie seulement une
+ colonne du bon type composite. Ceci n'est pas vraiment meilleur dans
+ cette situation mais c'est une alternative pratique dans certains cas
+ — par exemple, si nous avons besoin de calculer le résultat en
+ appelant une autre fonction qui renvoie la valeur composite désirée.
+ </para>
+
+ <para>
+ Nous pourrions appeler cette fonction directement de deux façons :
+
+ <screen>SELECT nouveau_emp();
+
+ nouveau_emp
+--------------------------
+ (None,1000.0,25,"(2,2)")
+
+SELECT * FROM nouveau_emp();
+
+ nom | salaire | age | cubicle
+-------+---------+-----+---------
+ Aucun | 1000.0 | 25 | (2,2)
+ </screen>
+
+ La deuxième façon est décrite plus complètement dans la <xref
+ linkend="xfunc-sql-table-functions"/>.
+ </para>
+
+ <para>
+ Quand vous utilisez une fonction qui renvoie un type composite, vous
+ pourriez vouloir seulement un champ (attribut) depuis ce résultat. Vous
+ pouvez le faire avec cette syntaxe :
+
+<screen>SELECT (nouveau_emp()).nom;
+
+ nom
+------
+ None
+</screen>
+
+ Les parenthèses supplémentaires sont nécessaires pour éviter une erreur de
+ l'analyseur. Si vous essayez de le faire sans, vous obtiendrez quelque chose
+ comme ceci :
+ <screen>SELECT nouveau_emp().nom;
+ERROR: syntax error at or near "."
+LINE 1: SELECT nouveau_emp().nom;
+ ^
+</screen>
+ </para>
+
+ <para>
+ Une autre option est d'utiliser la notation fonctionnelle pour extraire un
+ attribut. Une manière simple d'expliquer cela est de dire que nous pouvons
+ échanger les notations <literal>attribut(table)</literal> et
+ <literal>table.attribut</literal>.
+
+<screen>SELECT nom(nouveau_emp());
+
+ name
+------
+ None
+</screen>
+
+<screen>-- C'est la même chose que
+-- SELECT emp.nom AS leplusjeune FROM emp WHERE emp.age < 30;
+
+SELECT nom(emp) AS leplusjeune FROM emp WHERE age(emp) < 30;
+
+ leplusjeune
+-------------
+ Sam
+ Andy
+</screen>
+ </para>
+
+ <tip>
+ <para>
+ L'équivalence entre la notation fonctionnelle et la notation d'attribut
+ rend possible l'utilisation de fonctions sur des types composites pour
+ émuler les <quote>champs calculés</quote>.
+ <indexterm>
+ <primary>champ calculé</primary>
+ </indexterm>
+ <indexterm>
+ <primary>field</primary>
+ <secondary>computed</secondary>
+ </indexterm>
+ Par exemple, en utilisant la définition précédente pour
+ <literal>double_salaire(emp)</literal>, nous pouvons écrire
+<screen>SELECT emp.nom, emp.double_salaire FROM emp;
+</screen>
+
+ Une application utilisant ceci n'aurait pas besoin d'être consciente
+ directement que <literal>double_salaire</literal> n'est pas une vraie colonne
+ de la table (vous pouvez aussi émuler les champs calculés avec des
+ vues).
+ </para>
+ </tip>
+
+ <para>
+ Une autre façon d'utiliser une fonction renvoyant un type composite est
+ de l'appeler comme une fonction de table, comme décrit dans la <xref
+ linkend="xfunc-sql-table-functions"/>.
+ </para>
+ </sect2>
+
+ <sect2 id="xfunc-output-parameters">
+ <title>Fonctions <acronym>SQL</acronym> avec des paramètres en sortie</title>
+
+ <indexterm>
+ <primary>fonction</primary>
+ <secondary>paramètre en sortie</secondary>
+ </indexterm>
+
+ <para>
+ Une autre façon de décrire les résultats d'une fonction est de la
+ définir avec des <firstterm>paramètres en sortie</firstterm> comme dans cet
+ exemple :
+
+<screen>CREATE FUNCTION ajoute (IN x int, IN y int, OUT sum int)
+AS 'SELECT $1 + $2'
+LANGUAGE SQL;
+
+SELECT ajoute(3,7);
+ ajoute
+--------
+ 10
+(1 row)
+</screen>
+
+ Ceci n'est pas vraiment différent de la version d'<literal>ajoute</literal>
+ montrée dans la <xref linkend="xfunc-sql-base-functions"/>. La vraie valeur
+ des paramètres en sortie est qu'ils fournissent une façon agréable de
+ définir des fonctions qui renvoient plusieurs colonnes. Par exemple,
+
+<screen>CREATE FUNCTION ajoute_n_produit (x int, y int, OUT sum int, OUT product int)
+AS 'SELECT $1 + $2, $1 * $2'
+LANGUAGE SQL;
+
+ SELECT * FROM sum_n_product(11,42);
+ sum | product
+-----+---------
+ 53 | 462
+(1 row)
+</screen>
+
+ Ce qui est arrivé ici est que nous avons créé un type composite anonyme
+ pour le résultat de la fonction. L'exemple ci-dessus a le même résultat
+ final que
+
+<screen>CREATE TYPE produit_ajoute AS (somme int, produit int);
+
+CREATE FUNCTION ajoute_n_produit (int, int) RETURNS produit_ajoute
+AS 'SELECT $1 + $2, $1 * $2'
+LANGUAGE SQL;
+</screen>
+
+ mais ne pas avoir à s'embêter avec la définition séparée du type
+ composite est souvent agréable.
+ </para>
+
+ <para>
+ Notez que les paramètres en sortie ne sont pas inclus dans la liste
+ d'arguments lors de l'appel d'une fonction de ce type en SQL. Ceci
+ parce que <productname>PostgreSQL</productname> considère seulement les
+ paramètres en entrée pour définir la signature d'appel de la fonction.
+ Cela signifie aussi que seuls les paramètres en entrée sont importants
+ lors de références de la fonction pour des buts comme sa suppression.
+ Nous pouvons supprimer la fonction ci-dessus avec l'un des deux appels
+ ci-dessous :
+
+<screen>DROP FUNCTION ajoute_n_produit (x int, y int, OUT somme int, OUT produit int);
+DROP FUNCTION ajoute_n_produit (int, int);
+</screen>
+ </para>
+
+ <para>
+ Les paramètres peuvent être marqués comme <literal>IN</literal> (par défaut),
+ <literal>OUT</literal> ou <literal>INOUT</literal> ou
+ <literal>VARIADIC</literal>.
+ Un paramètre <literal>INOUT</literal>
+ sert à la fois de paramètre en entrée (il fait partie de la liste
+ d'arguments en appel) et comme paramètre de sortie (il fait partie du
+ type d'enregistrement résultat). Les paramètres
+ <literal>VARIADIC</literal> sont des paramètres en entrées, mais sont
+ traités spécifiquement comme indiqué ci-dessous.
+ </para>
+ </sect2>
+
+ <sect2 id="xfunc-sql-variadic-functions">
+ <title>Fonctions <acronym>SQL</acronym> avec un nombre variables d'arguments</title>
+
+ <indexterm>
+ <primary>fonction</primary>
+ <secondary>variadic</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>fonction variadic</primary>
+ </indexterm>
+
+ <para>
+ Les fonctions <acronym>SQL</acronym> peuvent accepter un nombre variable
+ d'arguments à condition que tous les arguments <quote>optionnels</quote>
+ sont du même type. Les arguments optionnels seront passés à la fonction
+ sous forme d'un tableau. La fonction est déclarée en marquant le dernier
+ paramètre comme <literal>VARIADIC</literal> ; ce paramètre doit être
+ déclaré de type tableau. Par exemple :
+
+<screen>
+CREATE FUNCTION mleast(VARIADIC numeric[]) RETURNS numeric AS $$
+ SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
+$$ LANGUAGE SQL;
+
+SELECT mleast(10, -1, 5, 4.4);
+ mleast
+--------
+ -1
+(1 row)
+</screen>
+
+ En fait, tous les arguments à la position ou après la position de
+ l'argument <literal>VARIADIC</literal> sont emballés dans un tableau à
+ une dimension, comme si vous aviez écrit
+
+<screen>
+SELECT mleast(ARRAY[10, -1, 5, 4.4]); -- doesn't work
+</screen>
+
+ Vous ne pouvez pas vraiment écrire cela, ou tout du moins cela ne
+ correspondra pas à la définition de la fonction. Un paramètre marqué
+ <literal>VARIADIC</literal> correspond à une ou plusieurs occurrences
+ de son type d'élément, et non pas de son propre type.
+ </para>
+
+ <para>
+ Sometimes it is useful to be able to pass an already-constructed array
+ to a variadic function; this is particularly handy when one variadic
+ function wants to pass on its array parameter to another one. You can
+ do that by specifying <literal>VARIADIC</literal> in the call:
+
+<screen>
+SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
+</screen>
+
+ This prevents expansion of the function's variadic parameter into its
+ element type, thereby allowing the array argument value to match
+ normally. <literal>VARIADIC</literal> can only be attached to the last
+ actual argument of a function call.
+ </para>
+ </sect2>
+
+ <sect2 id="xfunc-sql-parameter-defaults">
+ <title>Fonctions SQL <acronym>SQL</acronym> avec des valeurs par défaut
+ pour les arguments</title>
+
+ <indexterm>
+ <primary>fonction</primary>
+ <secondary>valeurs par défaut pour les arguments</secondary>
+ </indexterm>
+
+ <para>
+ Les fonctions peuvent être déclarées avec des valeurs par défaut pour
+ certains des paramètres en entrée ou pour tous. Les valeurs par défaut
+ sont insérées quand la fonction est appelée avec moins d'arguments que
+ à priori nécessaires. Comme les arguments peuvent seulement être omis
+ à partir de la fin de la liste des arguments, tous les paramètres après
+ un paramètres disposant d'une valeur par défaut disposeront eux-aussi
+ d'une valeur par défaut.
+ </para>
+
+ <para>
+ Par exemple :
+<screen>
+CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3)
+RETURNS int
+LANGUAGE SQL
+AS $$
+ SELECT $1 + $2 + $3;
+$$;
+
+SELECT foo(10, 20, 30);
+ foo
+-----
+ 60
+(1 row)
+
+SELECT foo(10, 20);
+ foo
+-----
+ 33
+(1 row)
+
+SELECT foo(10);
+ foo
+-----
+ 15
+(1 row)
+
+SELECT foo(); -- échec car il n'y a pas de valeur par défaut pour le premier argument
+ERROR: function foo() does not exist
+</screen>
+ Le signe <literal>=</literal> peut aussi être utilisé à la place du mot clé
+ <literal>DEFAULT</literal>,
+ </para>
+ </sect2>
+
+ <sect2 id="xfunc-sql-table-functions">
+ <title>Fonctions <acronym>SQL</acronym> comme sources de table</title>
+
+ <para>
+ Toutes les fonctions SQL peuvent être utilisées dans la clause
+ <literal>FROM</literal> d'une requête mais ceci est particulièrement utile pour les
+ fonctions renvoyant des types composite. Si la fonction est définie pour
+ renvoyer un type de base, la fonction table produit une table d'une seule
+ colonne. Si la fonction est définie pour renvoyer un type composite, la
+ fonction table produit une colonne pour chaque attribut du type composite.
+ </para>
+
+ <para>
+ Voici un exemple :
+
+<screen>CREATE TABLE foo (fooid int, foosousid int, foonom text);
+INSERT INTO foo VALUES (1, 1, 'Joe');
+INSERT INTO foo VALUES (1, 2, 'Ed');
+INSERT INTO foo VALUES (2, 1, 'Mary');
+
+CREATE FUNCTION recupfoo(int) RETURNS foo AS $$
+ SELECT * FROM foo WHERE fooid = $1;
+$$ LANGUAGE SQL;
+
+SELECT *, upper(foonom) FROM recupfoo(1) AS t1;
+
+ fooid | foosubid | foonom | upper
+-------+----------+--------+-------
+ 1 | 1 | Joe | JOE
+(1 row)
+</screen>
+
+ Comme le montre cet exemple, nous pouvons travailler avec les colonnes du
+ résultat de la fonction comme s'il s'agissait des colonnes d'une table
+ normale.
+</para>
+
+ <para>
+ Notez que nous n'obtenons qu'une ligne comme résultat de la fonction. Ceci
+ parce que nous n'avons pas utilisé l'instruction <literal>SETOF</literal>. Cette
+ instruction est décrite dans la prochaine section.
+ </para>
+ </sect2>
+
+ <sect2 id="xfunc-sql-functions-returning-set">
+ <title>Fonctions <acronym>SQL</acronym> renvoyant un ensemble </title>
+
+ <indexterm>
+ <primary>fonction</primary>
+ <secondary>avec SETOF</secondary>
+ </indexterm>
+
+ <para>
+ Quand une fonction SQL est déclarée renvoyer un <literal>SETOF
+ <replaceable>un_type</replaceable></literal>, la requête finale de
+ la fonction est complètement exécutée et chaque ligne extraite est renvoyée
+ en tant qu'élément de l'ensemble résultat.
+ </para>
+
+ <para>
+ Cette caractéristique est normalement utilisée lors de l'appel d'une
+ fonction dans une clause <literal>FROM</literal>. Dans ce cas, chaque ligne
+ renvoyée par la fonction devient une ligne de la table vue par la requête.
+ Par exemple, supposons que la table <literal>foo</literal> ait le même contenu que
+ précédemment et écrivons :
+
+<programlisting>CREATE FUNCTION recupfoo(int) RETURNS SETOF foo AS $$
+ SELECT * FROM foo WHERE fooid = $1;
+$$ LANGUAGE SQL;
+
+SELECT * FROM recupfoo(1) AS t1;
+</programlisting>
+
+ Alors nous obtenons :
+<screen> fooid | foosousid | foonom
+-------+-----------+--------
+ 1 | 1 | Joe
+ 1 | 2 | Ed
+(2 rows)
+</screen>
+ </para>
+
+ <para>
+ Il est aussi possible de renvoyer plusieurs lignes avec les colonnes
+ définies par des paramètres en sortie, comme ceci :
+
+<programlisting>
+CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int) RETURNS SETOF record AS $$
+ SELECT $1 + tab.y, tab.y * tab.y FROM tab;
+$$ LANGUAGE SQL;
+</programlisting>
+
+ Le point clé ici est que vous devez écrire <literal>RETURNS SETOF
+ record</literal> pour indiquer que la fonction renvoie plusieurs lignes
+ et non pas une seule. S'il n'y a qu'un paramètre en sortie, indiquez le
+ type de paramètre plutôt que <type>record</type>.
+ </para>
+
+ <para>
+ Actuellement, les fonctions renvoyant des ensembles peuvent aussi être
+ appelées dans la liste du select d'une requête. Pour chaque ligne générée
+ par la requête, la fonction renvoyant un ensemble est appelée et une ligne
+ est générée pour chaque élément de l'ensemble résultat. Notez cependant que
+ cette fonctionnalité est déconseillée et pourra être supprimée dans une
+ version future. Voici un exemple de fonction renvoyant un ensemble à
+ partir de la liste d'un SELECT :
+
+<screen>CREATE FUNCTION listeenfant(text) RETURNS SETOF text AS $$
+ SELECT nom FROM noeuds WHERE parent = $1
+$$ LANGUAGE SQL;
+
+SELECT * FROM noeuds;
+ nom | parent
+--------------+--------
+ Haut |
+ Enfant1 | Haut
+ Enfant2 | Haut
+ Enfant3 | Haut
+ Sous-Enfant1 | Enfant1
+ Sous-Enfant2 | Enfant1
+(6 rows)
+
+SELECT listeenfant('Haut');
+ listeenfant
+--------------
+ Enfant1
+ Enfant2
+ Enfant3
+(3 rows)
+
+SELECT nom, listeenfant(nom) FROM noeuds;
+ nom | listeenfant
+---------+--------------
+ Haut | Enfant1
+ Haut | Enfant2
+ Haut | Enfant3
+ Enfant1 | Sous-Enfant1
+ Enfant1 | Sous-Enfant2
+(5 rows)
+</screen>
+
+ Notez, dans le dernier <command>SELECT</command>, qu'aucune ligne n'est
+ renvoyée pour <literal>Enfant2</literal>, <literal>Enfant3</literal>, etc. C'est parce
+ que la fonction <function>listeenfant</function> renvoie un ensemble vide
+ pour ces arguments et ainsi aucune ligne n'est générée.
+ </para>
+
+ <note>
+ <para>
+ Si la dernière commande d'une fonction est <command>INSERT</command>,
+ <command>UPDATE</command> ou <command>DELETE</command> avec une clause
+ <literal>RETURNING</literal>, cette commande sera toujours exécutée
+ jusqu'à sa fin, même si la fonction n'est pas déclarée avec
+ <literal>SETOF</literal> ou que la requête appelante ne renvoie pas toutes
+ les lignes résultats. Toutes les lignes supplémentaires produites par la
+ clause <literal>RETURNING</literal> sont silencieusement abandonnées mais
+ les modifications de table sont pris en compte (et sont toutes terminées
+ avant que la fonction ne se termine).
+ </para>
+ </note>
+ </sect2>
+
+ <sect2 id="xfunc-sql-functions-returning-table">
+ <title>Fonctions <acronym>SQL</acronym> renvoyant <literal>TABLE</literal></title>
+
+ <indexterm>
+ <primary>fonction</primary>
+ <secondary>RETURNS TABLE</secondary>
+ </indexterm>
+
+ <para>
+ Il existe une autre façon de déclarer une fonction comme renvoyant un
+ ensemble de données. Cela passe par la syntaxe <literal>RETURNS
+ TABLE(<replaceable>colonnes</replaceable>)</literal>. C'est équivalent
+ à utiliser un ou plusieurs paramètres <literal>OUT</literal> et à marquer
+ la fonction comme renvoyant un <literal>SETOF record</literal> (ou
+ <literal>SETOF</literal> d'un type simple en sortie, comme approprié).
+ Cette notation est indiquée dans les versions récentes du standard SQL
+ et, du coup, devrait être plus portable que <literal>SETOF</literal>.
+ </para>
+
+ <para>
+ L'exemple précédent, sum-and-product, peut se faire aussi de la façon
+ suivante :
+
+<programlisting>
+CREATE FUNCTION sum_n_product_with_tab (x int) RETURNS TABLE(sum int, product int) AS $$
+ SELECT $1 + tab.y, $1 * tab.y FROM tab;
+$$ LANGUAGE SQL;
+</programlisting>
+
+ Il n'est pas autorisé d'utiliser explicitement des paramètres
+ <literal>OUT</literal> ou <literal>INOUT</literal> avec la notation
+ <literal>RETURNS TABLE</literal> — vous devez indiquer toutes les
+ colonnes en sortie dans la liste <literal>TABLE</literal>.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Fonctions <acronym>SQL</acronym> polymorphes</title>
+
+ <para>
+ Les fonctions <acronym>SQL</acronym> peuvent être déclarées pour accepter
+ et renvoyer les types <quote>polymorphe</quote> <type>anyelement</type>,
+ <type>anyarray</type>, <type>anynonarray</type> et <type>anyenum</type>.
+ Voir la <xref linkend="extend-types-polymorphic"/> pour une
+ explication plus approfondie. Voici une fonction polymorphe
+ <function>cree_tableau</function> qui construit un tableau à partir de
+ deux éléments de type arbitraire :
+
+<screen>CREATE FUNCTION cree_tableau(anyelement, anyelement) RETURNS anyarray AS $$
+ SELECT ARRAY[$1, $2];
+$$ LANGUAGE SQL;
+SELECT cree_tableau(1, 2) AS tableau_entier, cree_tableau('a'::text, 'b') AS
+tableau_texte;
+
+ tableau_entier | tableau_texte
+----------------+---------------
+ {1,2} | {a,b}
+(1 row)
+</screen>
+ </para>
+
+ <para>
+ Notez l'utilisation du transtypage <literal>'a'::text</literal> pour
+ spécifier le type <type>text</type> de l'argument. Ceci est nécessaire si
+ l'argument est une chaîne de caractères car, autrement, il serait traité
+ comme un type <type>unknown</type>, et un tableau de type
+ <type>unknown</type> n'est pas un type valide. Sans le transtypage, vous
+ obtiendrez ce genre d'erreur :
+
+<screen><computeroutput>ERROR: could not determine polymorphic type because input is UNKNOWN</computeroutput></screen>
+ </para>
+
+ <para>
+ Il est permis d'avoir des arguments polymorphes avec un type de renvoi
+ fixe, mais non l'inverse. Par exemple :
+
+<screen>CREATE FUNCTION est_plus_grand(anyelement, anyelement) RETURNS bool AS $$
+ SELECT $1 > $2;
+$$ LANGUAGE SQL;
+
+SELECT est_plus_grand(1, 2);
+ est_plus_grand
+----------------
+ f
+(1 row)
+
+CREATE FUNCTION fonction_invalide() RETURNS anyelement AS $$
+ SELECT 1;
+$$ LANGUAGE SQL;
+ERROR: cannot determine result datatype
+DETAIL: A function returning a polymorphic type must have at least one
+polymorphic argument.
+</screen>
+ </para>
+
+ <para>
+ Le polymorphisme peut être utilisé avec les fonctions qui ont des
+ arguments en sortie. Par exemple :
+<screen>CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray)
+AS 'select $1, array[$1,$1]' LANGUAGE SQL;
+
+SELECT * FROM dup(22);
+ f2 | f3
+----+---------
+ 22 | {22,22}
+(1 row)
+</screen>
+ </para>
+
+ <para>
+ Le polymorphisme peut aussi être utilisé avec des fonctions variadic.
+ Par exemple :
+<screen>
+CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
+ SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
+$$ LANGUAGE SQL;
+
+SELECT anyleast(10, -1, 5, 4);
+ anyleast
+----------
+ -1
+(1 row)
+
+SELECT anyleast('abc'::text, 'def');
+ anyleast
+----------
+ abc
+(1 row)
+
+CREATE FUNCTION concat(text, VARIADIC anyarray) RETURNS text AS $$
+ SELECT array_to_string($2, $1);
+$$ LANGUAGE SQL;
+
+SELECT concat('|', 1, 4, 2);
+ concat
+--------
+ 1|4|2
+(1 row)
+</screen>
+ </para>
+ </sect2>
+ </sect1>
+
+ <sect1 id="xfunc-overload">
+ <title>Surcharge des fonctions</title>
+
+ <indexterm zone="xfunc-overload">
+ <primary>surcharge</primary>
+ <secondary>fonctions</secondary>
+ </indexterm>
+
+ <para>
+ Plusieurs fonctions peuvent être définies avec le même nom SQL à condition
+ que les arguments soient différents. En d'autres termes, les noms de
+ fonction peuvent être <firstterm>surchargés</firstterm>. Quand une
+ requête est exécutée, le serveur déterminera la fonction à appeler à
+ partir des types de données des arguments et du nombre d'arguments. La
+ surcharge peut aussi être utilisée pour simuler des fonctions avec un
+ nombre variable d'arguments jusqu'à un nombre maximum fini.
+ </para>
+
+ <para>
+ Lors de la création d'une famille de fonctions surchargées, vous devriez
+ être attentif à ne pas créer d'ambiguïtés. Par exemple, avec les
+ fonctions :
+ <programlisting>CREATE FUNCTION test(int, real) RETURNS ...
+CREATE FUNCTION test(smallint, double precision) RETURNS ...</programlisting>
+ Savoir quelle fonction sera appelée avec une entrée triviale comme
+ <literal>test(1, 1.5)</literal> n'est pas immédiatement clair. Les
+ règles de résolution actuellement implémentées sont décrites dans le
+ <xref linkend="typeconv"/> mais il est déconseillé de concevoir un
+ système qui serait basé subtilement sur ce comportement.
+ </para>
+
+ <para>
+ Une fonction qui prend un seul argument d'un type composite devrait
+ généralement ne pas avoir le même nom que tout attribut (champ) de
+ ce type. Rappellez-vous que <literal>attribut(table)</literal> est
+ considéré comme équivalent à <literal>table.attribut</literal>. Dans
+ le cas où il existe une ambiguïté entre une fonction sur un type
+ composite et sur un attribut d'un type composite, l'attribut sera
+ toujours utilisé. Il est possible de contourner ce choix en
+ qualifiant le nom de la fonction avec celui du schéma (c'est-à-dire
+ <literal>schema.fonction(table)</literal>) mais il est préférable
+ d'éviter le problème en ne choisissant aucun nom conflictuel.
+ </para>
+
+ <para>
+ Un autre conflit possible se trouve entre les fonctions variadic et les
+ autres. En fait, il est possible de créer à la fois
+ <literal>foo(numeric)</literal> et <literal>foo(VARIADIC numeric[])</literal>.
+ Dans ce cas, il n'est pas simple de savoir lequel sera sélectionné lors
+ d'un appel avec un seul argument numérique, par exemple
+ <literal>foo(10.1)</literal>. La règle est que la fonction apparaissant
+ plsu tôt dans le chemin des schémas est utilisé. De même, si les deux
+ fonctions sont dans le même schéma, la non variadic est préféré.
+ </para>
+
+ <para>
+ Lors de la surcharge de fonctions en langage C, il existe une
+ contrainte supplémentaire : le nom C de chaque fonction dans la
+ famille des fonctions surchargées doit être différent des noms C de
+ toutes les autres fonctions, soit internes soit chargées dynamiquement
+ Si cette règle est violée, le comportement n'est pas portable. Vous
+ pourriez obtenir une erreur de l'éditeur de lien ou une des fonctions
+ sera appelée (habituellement l'interne). L'autre forme de clause
+ <literal>AS</literal> pour la commande SQL <command>CREATE
+ FUNCTION</command> découple le nom de la fonction SQL à partir du
+ nom de la fonction dans le code source C. Par exemple :
+<programlisting>CREATE FUNCTION test(int) RETURNS int
+ AS '<replaceable>filename</replaceable>', 'test_1arg'
+LANGUAGE C;
+CREATE FUNCTION test(int, int) RETURNS int
+ AS '<replaceable>filename</replaceable>', 'test_2arg'
+LANGUAGE C;
+</programlisting>
+ Les noms des fonctions C reflètent ici une des nombreuses conventions
+ possibles.
+ </para>
+ </sect1>
+
+ <sect1 id="xfunc-volatility">
+ <title>Catégories de volatilité des fonctions</title>
+
+ <indexterm zone="xfunc-volatility">
+ <primary>volatilité</primary>
+ <secondary>fonctions</secondary>
+ </indexterm>
+ <indexterm zone="xfunc-volatility">
+ <primary>VOLATILE</primary>
+ </indexterm>
+ <indexterm zone="xfunc-volatility">
+ <primary>STABLE</primary>
+ </indexterm>
+ <indexterm zone="xfunc-volatility">
+ <primary>IMMUTABLE</primary>
+ </indexterm>
+
+ <para>
+ Chaque fonction a une classification de volatilité
+ (<firstterm>volatility</firstterm>) comprenant
+ <literal>VOLATILE</literal>, <literal>STABLE</literal> ou <literal>IMMUTABLE</literal>.
+ <literal>VOLATILE</literal> est la valeur par défaut si la commande
+ <xref linkend="sql-createfunction" endterm="sql-createfunction-title"/> ne
+ spécifie pas de catégorie. La catégorie de volatilité est une promesse
+ à l'optimiseur sur le comportement de la fonction :
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Une fonction <literal>VOLATILE</literal> peut tout faire, y compris modifier
+ la base de données. Elle peut renvoyer différents
+ résultats sur des appels successifs avec les mêmes arguments.
+ L'optimiseur ne fait aucune supposition sur le comportement de telles
+ fonctions. Une requête utilisant une fonction volatile ré-évaluera la
+ fonction à chaque ligne où sa valeur est nécessaire.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Une fonction <literal>STABLE</literal> ne peut pas modifier la base de
+ données et est garantie de renvoyer les mêmes résultats si elle est
+ appelée avec les mêmes arguments pour toutes les lignes à l'intérieur
+ d'une même instruction. Cette catégorie permet à l'optimiseur d'optimiser
+ plusieurs appels de la fonction dans une seule requête. En particulier,
+ vous pouvez utiliser en toute sécurité une expression contenant une
+ telle fonction dans une condition de parcours d'index (car un parcours
+ d'index évaluera la valeur de la comparaison une seule fois, pas une
+ fois pour chaque ligne, utiliser une fonction <literal>VOLATILE</literal> dans
+ une condition de parcours d'index n'est pas valide).
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Une fonction <literal>IMMUTABLE</literal> ne peut pas modifier la base de
+ données et est garantie de toujours renvoyer les mêmes résultats si
+ elle est appelée avec les mêmes arguments. Cette catégorie permet à
+ l'optimiseur de pré-évaluer la fonction quand une requête l'appelle
+ avec des arguments constants. Par exemple, une requête comme
+ <literal>SELECT ... WHERE x = 2 + 2</literal> peut être simplifiée pour
+ obtenir <literal>SELECT ... WHERE x = 4</literal> car la fonction sous-jacente
+ de l'opérateur d'addition est indiquée <literal>IMMUTABLE</literal>.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </para>
+
+ <para>
+ Pour une meilleure optimisation des résultats, vous devez mettre un label
+ sur les fonctions avec la catégorie la plus volatile valide pour elles.
+ </para>
+
+ <para>
+ Toute fonction avec des effets de bord <emphasis>doit</emphasis> être indiquée
+ comme <literal>VOLATILE</literal>, de façon à ce que les appels ne puissent pas
+ être optimisés. Même une fonction sans effets de bord doit être indiquée
+ comme <literal>VOLATILE</literal> si sa valeur peut changer à l'intérieur
+ d'une seule requête ; quelques exemples sont <literal>random()</literal>,
+ <literal>currval()</literal>, <literal>timeofday()</literal>.
+ </para>
+
+ <para>
+ Un autre exemple important est que la famille de fonctions
+ <function>current_timestamp</function> est qualifiée comme
+ <literal>STABLE</literal> car leurs valeurs ne changent pas à l'intérieur
+ d'une transaction.
+ </para>
+
+ <para>
+ Il y a relativement peu de différences entre les catégories
+ <literal>STABLE</literal> et <literal>IMMUTABLE</literal> en considérant les requêtes
+ interactives qui sont planifiées et immédiatement exécutées : il
+ importe peu que la fonction soit exécutée une fois lors de la
+ planification ou une fois au lancement de l'exécution de la requête mais
+ cela fait une grosse différence si le plan est sauvegardé et utilisé plus
+ tard. Placer un label <literal>IMMUTABLE</literal> sur une fonction quand elle
+ ne l'est pas vraiment pourrait avoir comme conséquence de la considérer
+ prématurément comme une constante lors de la planification et résulterait en une valeur
+ erronée lors d'une utilisation ultérieure de ce plan d'exécution.
+ C'est un danger qui arrive lors de l'utilisattion d'instructions préparées
+ ou avec l'utilisation de langages de fonctions mettant les plans d'exécutions
+ en cache (comme
+ <application>PL/pgSQL</application>).
+ </para>
+
+ <para>
+ Pour les fonctions écrites en SQL ou dans tout autre langage de procédure
+ standard, la catégorie de volatibilité détermine une deuxième propriété
+ importante, à savoir la visibilité de toute modification de données
+ effectuées par la commande SQL qui a appelé la fonction. Une fonction
+ <literal>VOLATILE</literal> verra les changements, une fonction
+ <literal>STABLE</literal> ou <literal>IMMUTABLE</literal> ne les verra pas.
+ Ce comportement est implantée en utilisant le comportement par images de
+ MVCC (voir <xref linkend="mvcc"/>) : les fonctions
+ <literal>STABLE</literal> et <literal>IMMUTABLE</literal> utilisent une
+ image établie au lancement de la requête appelante alors que les fonctions
+ <literal>VOLATILE</literal> obtiennent une image fraiche au début de chaque
+ requête qu'elles exécutent.
+ </para>
+
+ <note>
+ <para>
+ Les fonctions écrites en C peuvent gérer les images de la façon qu'elles
+ le souhaitent, mais il est préférable de coder les fonctions C de la même
+ façon.
+ </para>
+ </note>
+
+ <para>
+ À cause du comportement à base d'images, une fonction contenant seulement des commandes
+ <command>SELECT</command> peut être indiquée <literal>STABLE</literal> en toute sécurité
+ même s'il sélectionne des données à partir de tables qui pourraient
+ avoir subi des modifications entre temps par des requêtes concurrentes.
+ <productname>PostgreSQL</productname> exécutera toutes les commandes d'une fonction
+ <literal>STABLE</literal> en utilisant l'image établie par la requête appelante et
+ n'aura qu'une vision figée de la base de données au cours de la requête.
+ </para>
+
+ <para>
+ Ce même comportement d'images est utilisé pour les commandes
+ <command>SELECT</command> à l'intérieur de fonctions <literal>IMMUTABLE</literal>. Il
+ est généralement déconseillé de sélectionner des tables de la base de
+ données à l'intérieur de fonctions <literal>IMMUTABLE</literal> car
+ l'immutabilité sera rompue si le contenu de la table change. Néanmoins,
+ <productname>PostgreSQL</productname> ne vous force pas à ne pas le faire.
+ </para>
+
+ <para>
+ Une erreur commune est de placer un label sur une fonction
+ <literal>IMMUTABLE</literal> quand son résultat dépend d'un paramètre de
+ configuration. Par exemple, une fonction qui manipule des types date/heure
+ pourrait bien avoir des résultats dépendant du paramètre
+ <xref linkend="guc-timezone"/>. Pour être sécurisées, de telles
+ fonctions devraient avoir le label <literal>STABLE</literal> à la place.
+ </para>
+
+ <note>
+ <para>
+ Avant <productname>PostgreSQL</productname> version 8.0, le prérequis
+ que les fonctions <literal>STABLE</literal> et
+ <literal>IMMUTABLE</literal> ne pouvaient pas modifier la base de données
+ n'était pas contraint par le système. Les versions 8.0 et ultérieures le
+ contraignent en
+ réclamant que les fonctions SQL et les fonctions de langages de
+ procédures de ces catégories ne contiennent pas de commandes SQL autre
+ que <command>SELECT</command> (ceci n'a pas été complètement testé car de
+ telles fonctions pourraient toujours appeler des fonctions
+ <literal>VOLATILE</literal> qui modifient la base de données. Si vous le
+ faites, vous trouverez que la fonction <literal>STABLE</literal> ou
+ <literal>IMMUTABLE</literal> n'est pas au courant des modifications
+ effectuées sur la base de données par la fonction appelée, car elles
+ sont cachées depuis son image).
+ </para>
+ </note>
+ </sect1>
+
+ <sect1 id="xfunc-pl">
+ <title>Fonctions en langage de procédures</title>
+
+ <para>
+ <productname>PostgreSQL</productname> autorise l'écriture de fonctions
+ définies par l'utilisateur dans d'autres langages que SQL et C. Ces autres
+ langages sont appelés des <firstterm>langages de procédure</firstterm>
+ (<acronym>PL</acronym>). Les langages de procédures ne sont pas compilés dans le
+ serveur <productname>PostgreSQL</productname> ; ils sont fournis comme
+ des modules chargeables. Voir le <xref linkend="xplang"/> et les chapitres
+ suivants pour plus d'informations.
+ </para>
+
+ <para>
+ Il y a actuellement quatre langages de procédures disponibles dans la
+ distribution <productname>PostgreSQL</productname> standard :
+ <application>PL/pgSQL</application>, <application>PL/Tcl</application>,
+ <application>PL/Perl</application> et <application>PL/Python</application>.
+
+ Référez-vous au <xref linkend="xplang"/> pour plus d'informations. D'autres
+ langages peuvent être définis par les utilisateurs. Les bases du
+ développement d'un nouveau langage de procédures sont traitées dans le <xref
+ linkend="plhandler"/>.
+ </para>
+
+ </sect1>
+
+ <sect1 id="xfunc-internal">
+ <title>Fonctions internes</title>
+
+ <indexterm zone="xfunc-internal"><primary>fonction</primary><secondary>interne</secondary></indexterm>
+
+ <para>
+ Les fonctions internes sont des fonctions écrites en C qui ont été liées de
+ façon statique dans le serveur <productname>PostgreSQL</productname>. Le
+ <quote>corps</quote> de la définition de la fonction spécifie le nom en
+ langage C de la fonction, qui n'est pas obligatoirement le même que le nom
+ déclaré pour l'utilisation en SQL (pour des raisons de rétro compatibilité,
+ un corps vide est accepté pour signifier que le nom de la fonction en
+ langage C est le même que le nom SQL).
+
+ </para>
+
+ <para>
+ Normalement, toutes les fonctions internes présentes dans le serveur sont
+ déclarées pendant l'initialisation du groupe de base de données
+ (<command>initdb</command>) mais un utilisateur peut utiliser la commande
+ <command>CREATE FUNCTION</command> pour créer des noms d'alias
+ supplémentaires pour une fonction interne. Les fonctions internes sont
+ déclarées dans la commande <command>CREATE FUNCTION</command> avec le nom
+ de langage <literal>internal</literal>. Par exemple, pour créer un alias
+ de la fonction <function>sqrt</function> :
+
+<programlisting>CREATE FUNCTION racine_carree(double precision) RETURNS double precision AS
+'dsqrt'
+LANGUAGE internal STRICT;
+</programlisting>
+
+ (la plupart des fonctions internes doivent être déclarées
+ <quote>STRICT</quote>)
+ </para>
+
+ <note>
+ <para>
+ Toutes les fonctions <quote>prédéfinies</quote> ne sont pas internes (au
+ sens explicité ci-dessus). Quelques fonctions prédéfinies sont écrites en
+ SQL.
+ </para>
+ </note>
+ </sect1>
+
+ <sect1 id="xfunc-c">
+ <title>Fonctions en langage C</title>
+
+ <indexterm zone="xfunc-c">
+ <primary>fonction</primary>
+ <secondary>définie par l'utilisateur</secondary>
+ <tertiary>en C</tertiary>
+ </indexterm>
+
+ <para>
+ Les fonctions définies par l'utilisateur peuvent être écrites en C (ou dans
+ un langage pouvant être rendu compatible avec C, comme le C++). Ces fonctions
+ sont compilées en objets dynamiques chargeables (encore appelés
+ bibliothèques partagées) et sont chargées par le serveur à la demande. Cette
+ caractéristique de chargement dynamique est ce qui distingue les fonctions
+ en <quote>langage C</quote> des fonctions <quote>internes</quote> — les véritables
+ conventions de codage sont essentiellement les mêmes pour les deux (c'est
+ pourquoi la bibliothèque standard de fonctions internes est une source
+ abondante d'exemples de code pour les fonctions C définies par
+ l'utilisateur).
+ </para>
+
+ <para>
+ Deux différentes conventions d'appel sont actuellement en usage pour les
+ fonctions C. La plus récente, <quote>version 1</quote>,
+ est indiquée en écrivant une macro d'appel
+ <literal>PG_FUNCTION_INFO_V1()</literal> comme illustré ci-après. L'absence
+ d'une telle macro indique une fonction écrite selon l'ancien style
+ (<quote>version 0</quote>). Le nom de langage spécifié dans la commande
+ <command>CREATE FUNCTION</command> est <literal>C</literal> dans les deux
+ cas. Les fonctions suivant l'ancien style sont maintenant déconseillées en
+ raison de problèmes de portabilité et d'un manque de fonctionnalité mais
+ elles sont encore supportées pour des raisons de compatibilité.
+ </para>
+
+ <sect2 id="xfunc-c-dynload">
+ <title>Chargement dynamique</title>
+
+ <indexterm zone="xfunc-c-dynload">
+ <primary>dynamic loading</primary>
+ </indexterm>
+
+ <para>
+ La première fois qu'une fonction définie par l'utilisateur dans un fichier
+ objet particulier chargeable est appelée dans une session, le chargeur
+ dynamique charge ce fichier objet en mémoire de telle sorte que la fonction
+ peut être appelée. La commande <command>CREATE FUNCTION</command> pour une
+ fonction en C définie par l'utilisateur doit par conséquent spécifier deux
+ éléments d'information pour la fonction : le nom du fichier objet
+ chargeable et le nom en C (lien symbolique) de la fonction spécifique à
+ appeler à l'intérieur de ce fichier objet. Si le nom en C n'est pas
+ explicitement spécifié, il est supposé être le même que le nom de la
+ fonction SQL.
+ </para>
+
+ <para>
+ L'algorithme suivant, basé sur le nom donné dans la commande
+ <command>CREATE FUNCTION</command>, est utilisé pour localiser le fichier
+ objet partagé :
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Si le nom est un chemin absolu, le fichier est chargé.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Si le nom commence par la chaîne <literal>$libdir</literal>, cette chaîne
+ est remplacée par le nom du répertoire de la bibliothèque du paquetage
+ <productname>PostgreSQL</productname>, qui est déterminé au moment de la
+ compilation. <indexterm><primary>$libdir</primary></indexterm>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Si le nom ne contient pas de partie répertoire, le fichier est recherché
+ par le chemin spécifié dans la variable de configuration
+ <xref linkend="guc-dynamic-library-path"/>.
+ <indexterm><primary>dynamic_library_path</primary></indexterm>
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Dans les autres cas, (nom de fichier non trouvé dans le chemin ou ne
+ contenant pas de partie répertoire non absolu), le chargeur dynamique
+ essaiera d'utiliser le nom donné, ce qui échouera très vraisemblablement
+ (dépendre du répertoire de travail en cours n'est pas fiable).
+ </para>
+ </listitem>
+ </orderedlist>
+
+ Si cette séquence ne fonctionne pas, l'extension pour les noms de fichier
+ des bibliothèques partagées spécifique à la plateforme (souvent
+ <filename>.so</filename>) est ajoutée au nom attribué et la séquence est à
+ nouveau tentée. En cas de nouvel échec, le chargement échoue.
+ </para>
+
+ <para>
+ Il est recommandé de localiser les bibliothèques partagées soit relativement
+ à <literal>$libdir</literal> ou via le chemin dynamique des bibliothèques.
+ Ceci simplifie les mises à jour de versions si la nouvelle installation est
+ à un emplacement différent. Le répertoire actuel représenté par
+ <literal>$libdir</literal> est trouvable avec la commande
+ <literal>pg_config --pkglibdir</literal>.
+ </para>
+
+ <para>
+ L'identifiant utilisateur sous lequel fonctionne le serveur
+ <productname>PostgreSQL</productname> doit pouvoir suivre le chemin
+ jusqu'au fichier que vous essayez de charger. Une erreur fréquente revient
+ à définir le fichier ou un répertoire supérieur comme non lisible et/ou
+ non exécutable par l'utilisateur <systemitem>postgres</systemitem>.
+ </para>
+
+ <para>
+ Dans tous les cas, le nom de fichier donné dans la commande <command>CREATE
+ FUNCTION</command> est enregistré littéralement dans les catalogues
+ systèmes, de sorte que, si le fichier doit être à nouveau chargé, la même
+ procédure sera appliquée.
+ </para>
+
+ <note>
+ <para>
+ <productname>PostgreSQL</productname> ne compilera pas une fonction C
+ automatiquement. Le fichier objet doit être compilé avant d'être référencé
+ dans une commande <command>CREATE FUNCTION</command>. Voir la <xref
+ linkend="dfunc"/> pour des informations complémentaires.
+ </para>
+ </note>
+
+ <indexterm zone="xfunc-c-dynload">
+ <primary>bloc magique</primary>
+ </indexterm>
+
+ <para>
+ Pour s'assurer qu'un fichier objet chargeable dynamiquement n'est pas chargé
+ dans un serveur incompatible, <productname>PostgreSQL</productname> vérifie
+ que le fichier contient un <quote>bloc magique</quote> avec un contenu
+ approprié. Ceci permet au serveur de détecter les incompatibilités évidentes
+ comme du code compilet pour une version majeure différente de
+ <productname>PostgreSQL</productname>. Un bloc magique est requis à partir de
+ <productname>PostgreSQL</productname> 8.2. Pour inclure un bloc magique,
+ écrivez ceci dans un (et seulement un) des fichiers source du module, après
+ avoir inclus l'en-tête <filename>fmgr.h</filename> :
+
+<programlisting>#ifdef PG_MODULE_MAGIC
+PG_MODULE_MAGIC;
+#endif
+</programlisting>
+
+ Le test <literal>#ifdef</literal> peut être omis si le code n'a pas besoin
+ d'être compilé avec des versions de <productname>PostgreSQL</productname>
+ antérieures à la 8.2.
+ </para>
+
+ <para>
+ Après avoir été utilisé pour la première fois, un fichier objet chargé
+ dynamiquement est conservé en mémoire. Les futurs appels de fonction(s) dans
+ ce fichier pendant la même session provoqueront seulement une légère
+ surcharge due à la consultation d'une table de symboles. Si vous devez
+ forcer le chargement d'un fichier objet, par exemple après une
+ recompilation, utilisez la commande <xref
+ linkend="sql-load" endterm="sql-load-title"/> ou commencez une
+ nouvelle session.
+ </para>
+
+ <indexterm zone="xfunc-c-dynload">
+ <primary>_PG_init</primary>
+ </indexterm>
+ <indexterm zone="xfunc-c-dynload">
+ <primary>_PG_fini</primary>
+ </indexterm>
+ <indexterm zone="xfunc-c-dynload">
+ <primary>fonction d'initialisation de la bibliothèque</primary>
+ </indexterm>
+ <indexterm zone="xfunc-c-dynload">
+ <primary>fonction de terminaison de la bibliothèque</primary>
+ </indexterm>
+
+ <para>
+ De façon optionnelle, un fichier chargé dynamiquement peut contenir des
+ fonctions d'initialisation et de terminaison. Si le fichier inclut une
+ fonction nommée <function>_PG_init</function>, cette fonction sera appelée
+ immédiatement après le chargement du fichier. La fonction ne reçoit
+ aucun paramètre et doit renvoyer void. Si le fichier inclut une fonction
+ nommée <function>_PG_fini</function>, cette fonction sera appelée tout juste
+ avant le déchargement du fichier. De la même façon, la fonction ne reçoit
+ aucun paramètre et doit renvoyer void. Notez que <function>_PG_fini</function>
+ sera seulement appelée lors du déchargement du fichier, pas au moment de la
+ fin du processus. (Actuellement, un déchargement n'intervient que dans le
+ contexte d'un re-chargement du fichier suite à la commande explicite
+ <command>LOAD</command>.)
+ </para>
+
+ </sect2>
+
+ <sect2 id="xfunc-c-basetype">
+ <title>Types de base dans les fonctions en langage C</title>
+
+ <indexterm zone="xfunc-c-basetype">
+ <primary>type de données</primary>
+ <secondary>organisation interne</secondary>
+ </indexterm>
+
+ <para>
+ Pour savoir comment écrire des fonctions en langage C, vous devez savoir
+ comment <productname>PostgreSQL</productname> représente en interne les
+ types de données de base et comment elles peuvent être passés vers et
+ depuis les fonctions. En interne, <productname>PostgreSQL</productname>
+ considère un type de base comme un <quote>blob de mémoire</quote>. Les
+ fonctions que vous définissez sur un type définissent à leur tour la façon
+ que <productname>PostgreSQL</productname> opère sur lui. C'est-à-dire
+ que <productname>PostgreSQL</productname> ne fera que conserver et retrouver
+ les données sur le disque et utilisera votre fonction pour entrer, traiter
+ et restituer les données.
+ </para>
+
+ <para>
+ Les types de base peuvent avoir un des trois formats internes
+ suivants :
+ <itemizedlist>
+ <listitem>
+ <para>
+ passage par valeur, longueur fixe ;
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ passage par référence, longueur fixe ;
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ passage par référence, longueur variable.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+
+ <para>
+ Les types par valeur peuvent seulement avoir une longueur de 1, 2 ou 4
+ octets (également 8 octets si <literal>sizeof(Datum)</literal> est de huit
+ octets sur votre machine). Vous devriez être attentif lors de la
+ définition de vos types de sorte à qu'ils aient la même taille sur toutes
+ les architectures. Par exemple, le type <literal>long</literal> est
+ dangereux car il a une taille de quatre octets sur certaines machines et
+ huit octets sur d'autres, alors que le type <type>int</type> est de quatre
+ octets sur la plupart des machines Unix. Une implémentation raisonnable du
+ type <type>int4</type> sur une machine Unix pourrait être
+
+ <programlisting>/* entier sur quatre octets, passé par valeur */
+typedef int int4;
+</programlisting>
+
+ </para>
+
+ <para>
+ D'autre part, les types à longueur fixe d'une taille quelconque peuvent
+ être passés par référence. Par exemple, voici l'implémentation d'un type
+ <productname>PostgreSQL</productname> :
+
+<programlisting>/* structure de 16 octets, passée par référence */
+typedef struct
+{
+ double x, y;
+} Point;
+</programlisting>
+
+ Seuls des pointeurs vers de tels types peuvent être utilisés en les passant
+ dans et hors des fonctions <productname>PostgreSQL</productname>. Pour
+ renvoyer une valeur d'un tel type, allouez la quantité appropriée de
+ mémoire avec <literal>palloc</literal>, remplissez la mémoire allouée et
+ renvoyez un pointeur vers elle (de plus, si vous souhaitez seulement
+ renvoyer la même valeur qu'un de vos arguments en entrée qui se trouve du
+ même type, vous pouvez passer le <literal>palloc</literal>
+ supplémentaire et simplement renvoyer le pointeur vers la valeur en
+ entrée).
+ </para>
+
+ <para>
+ Enfin, tous les types à longueur variable doivent aussi être passés par
+ référence. Tous les types à longueur variable doivent commencer avec un
+ champ d'une longueur d'exactement quatre octets et toutes les données
+ devant être stockées dans ce type doivent être localisées dans la mémoire à
+ la suite immédiate de ce champ longueur. Le champ longueur contient la
+ longueur totale de la structure, c'est-à-dire incluant la longueur du
+ champ longueur lui-même.
+ </para>
+
+ <warning>
+ <para>
+ Ne <emphasis>jamais</emphasis> modifier le contenu d'une valeur en entrée passée
+ par référence. Si vous le faites, il y a de forts risques pour que
+ vous réussissiez à corrompre les données sur disque car le pointeur que
+ vous avez reçu pourrait bien pointer directement vers un tampon disque.
+ La seule exception à cette règle est expliquée dans la <xref
+ linkend="xaggr"/>.
+ </para>
+ </warning>
+
+ <para>
+ Comme exemple, nous pouvons définir le type <type>text</type> comme
+ ceci :
+
+<programlisting>typedef struct {
+ int4 length;
+ char data[1];
+} text;
+</programlisting>
+
+ Il est évident que le champ déclaré ici n'est pas assez long pour contenir
+ toutes les chaînes possibles. Comme il est impossible de déclarer une
+ structure de taille variable en <acronym>C</acronym>, nous nous appuyons
+ sur le fait que le compilateur <acronym>C</acronym> ne vérifie pas la
+ plage des indices de tableau. Nous allouons juste la quantité d'espace
+ nécessaire et ensuite nous accédons au tableau comme s'il avait été déclaré
+ avec la bonne longueur (c'est une astuce courante que vous pouvez trouver
+ dans beaucoup de manuels de C).
+ </para>
+
+ <para>
+ En manipulant les types à longueur variable, nous devons être attentifs à
+ allouer la quantité correcte de mémoire et à fixer correctement le champ
+ longueur. Par exemple, si nous voulons stocker 40 octets dans une structure
+ <structname>text</structname>, nous devrions utiliser un fragment de code comme
+ celui-ci :
+
+<programlisting><![CDATA[#include "postgres.h"
+...
+char buffer[40]; /* notre donnée source */
+...
+text *destination = (text *) palloc(VARHDRSZ + 40);
+destination->length = VARHDRSZ + 40;
+memcpy(destination->data, buffer, 40);
+...
+]]></programlisting>
+
+ <literal>VARHDRSZ</literal> est équivalent à <literal>sizeof(int4)</literal> mais
+ est considéré comme une meilleure tournure de référence à la taille de
+ l'overhead pour un type à longueur variable.
+ </para>
+
+ <para>
+ Le <xref linkend="xfunc-c-type-table"/> spécifie la correspondance entre
+ les types C et les types SQL quand on écrit une fonction en langage C
+ utilisant les types internes de <productname>PostgreSQL</productname>. La colonne
+ <quote>Défini dans</quote> donne le fichier d'en-tête devant être inclus
+ pour accéder à la définition du type (la définition effective peut se
+ trouver dans un fichier différent inclus dans le fichier indiqué. Il
+ est recommandé que les utilisateurs s'en tiennent à l'interface définie).
+ Notez que vous devriez toujours inclure <filename>postgres.h</filename> en
+ premier dans tout fichier source car il déclare un grand nombre d'éléments
+ dont vous aurez besoin de toute façon.
+ </para>
+
+ <table tocentry="1" id="xfunc-c-type-table">
+ <title>Équivalence des types C et des types SQL intégrés</title>
+ <tgroup cols="3">
+ <colspec colnum="1" colwidth="0.7*"/>
+ <colspec colnum="2" colwidth="0.5*"/>
+ <colspec colnum="3" colwidth="1.8*"/>
+ <thead>
+ <row>
+ <entry>
+ Type SQL
+ </entry>
+ <entry>
+ Type C
+ </entry>
+ <entry>
+ Défini dans
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><type>abstime</type></entry>
+ <entry><type>AbsoluteTime</type></entry>
+ <entry><filename>utils/nabstime.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>boolean</type></entry>
+ <entry><type>bool</type></entry>
+ <entry><filename>postgres.h</filename> (intégration au
+ compilateur)</entry>
+ </row>
+ <row>
+ <entry><type>box</type></entry>
+ <entry><type>BOX*</type></entry>
+ <entry><filename>utils/geo_decls.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>bytea</type></entry>
+ <entry><type>bytea*</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>"char"</type></entry>
+ <entry><type>char</type></entry>
+ <entry>(intégré au compilateur)</entry>
+ </row>
+ <row>
+ <entry><type>character</type></entry>
+ <entry><type>BpChar*</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>cid</type></entry>
+ <entry><type>CommandId</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>date</type></entry>
+ <entry><type>DateADT</type></entry>
+ <entry><filename>utils/date.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>smallint</type> (<type>int2</type>)</entry>
+ <entry><type>int2</type> or <type>int16</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>int2vector</type></entry>
+ <entry><type>int2vector*</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>integer</type> (<type>int4</type>)</entry>
+ <entry><type>int4</type> or <type>int32</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>real</type> (<type>float4</type>)</entry>
+ <entry><type>float4*</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>double precision</type> (<type>float8</type>)</entry>
+ <entry><type>float8*</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>interval</type></entry>
+ <entry><type>Interval*</type></entry>
+ <entry><filename>utils/timestamp.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>lseg</type></entry>
+ <entry><type>LSEG*</type></entry>
+ <entry><filename>utils/geo_decls.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>name</type></entry>
+ <entry><type>Name</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>oid</type></entry>
+ <entry><type>Oid</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>oidvector</type></entry>
+ <entry><type>oidvector*</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>path</type></entry>
+ <entry><type>PATH*</type></entry>
+ <entry><filename>utils/geo_decls.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>point</type></entry>
+ <entry><type>POINT*</type></entry>
+ <entry><filename>utils/geo_decls.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>regproc</type></entry>
+ <entry><type>regproc</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>reltime</type></entry>
+ <entry><type>RelativeTime</type></entry>
+ <entry><filename>utils/nabstime.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>text</type></entry>
+ <entry><type>text*</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>tid</type></entry>
+ <entry><type>ItemPointer</type></entry>
+ <entry><filename>storage/itemptr.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>time</type></entry>
+ <entry><type>TimeADT</type></entry>
+ <entry><filename>utils/date.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>time with time zone</type></entry>
+ <entry><type>TimeTzADT</type></entry>
+ <entry><filename>utils/date.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>timestamp</type></entry>
+ <entry><type>Timestamp*</type></entry>
+ <entry><filename>utils/timestamp.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>tinterval</type></entry>
+ <entry><type>TimeInterval</type></entry>
+ <entry><filename>utils/nabstime.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>varchar</type></entry>
+ <entry><type>VarChar*</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ <row>
+ <entry><type>xid</type></entry>
+ <entry><type>TransactionId</type></entry>
+ <entry><filename>postgres.h</filename></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Maintenant que nous avons passé en revue toutes les structures possibles
+ pour les types de base, nous pouvons donner quelques exemples de vraies
+ fonctions.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Conventions d'appel de la version 0</title>
+
+ <para>
+ Nous présentons l'<quote>ancien style</quote> de convention d'appel en
+ premier — bien que cette approche soit maintenant déconseillée, elle est
+ plus facile à maîtriser au début. Dans la méthode version-0, les arguments
+ et résultats de la fonction C sont simplement déclarés dans le style C
+ normal mais en faisant attention à utiliser la représentation C de chaque
+ type de données SQL comme montré ci-dessus.
+ </para>
+
+ <para>
+ Voici quelques exemples :
+
+<programlisting><![CDATA[#include "postgres.h"
+#include <string.h>
+
+/* par valeur */
+
+int
+add_one(int arg)
+{
+ return arg + 1;
+}
+
+/* par référence, taille fixe */
+
+float8 *
+add_one_float8(float8 *arg)
+{
+ float8 *result = (float8 *) palloc(sizeof(float8));
+
+ *result = *arg + 1.0;
+
+ return result;
+}
+
+Point *
+makepoint(Point *pointx, Point *pointy)
+{
+ Point *new_point = (Point *) palloc(sizeof(Point));
+
+ new_point->x = pointx->x;
+ new_point->y = pointy->y;
+
+ return new_point;
+}
+
+/* par référence, taille variable */
+
+text *
+copytext(text *t)
+{
+ /*
+ * VARSIZE est la taille totale de la structure en octets.
+ */
+ text *new_t = (text *) palloc(VARSIZE(t));
+ SET_VARSIZE(new_t, VARSIZE(t));
+ /*
+ * VARDATA est un pointeur sur la région de données de la structure.
+ */
+ memcpy((void *) VARDATA(new_t), /* destination */
+ (void *) VARDATA(t), /* source */
+ VARSIZE(t) - VARHDRSZ); /* nombre d'octets */
+ return new_t;
+}
+
+text *
+concat_text(text *arg1, text *arg2)
+{
+ int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
+ text *new_text = (text *) palloc(new_text_size);
+
+ SET_VARSIZE(new_text, new_text_size);
+ memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
+ memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ),
+ VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
+ return new_text;
+}
+]]></programlisting>
+ </para>
+
+ <para>
+ En supposant que le code ci-dessus ait été écrit dans le fichier
+ <filename>funcs.c</filename> et compilé en objet partagé, nous pourrions
+ définir les fonctions pour <productname>PostgreSQL</productname> avec des
+ commandes comme ceci :
+
+<programlisting>CREATE FUNCTION add_one(integer) RETURNS integer
+ AS '<replaceable>DIRECTORY</replaceable>/funcs', 'add_one'
+ LANGUAGE C STRICT;
+
+-- notez la surcharge du nom de la fonction SQL "add_one"
+CREATE FUNCTION add_one(double precision) RETURNS double precision
+ AS '<replaceable>DIRECTORY</replaceable>/funcs', 'add_one_float8'
+ LANGUAGE C STRICT;
+
+CREATE FUNCTION makepoint(point, point) RETURNS point
+ AS '<replaceable>DIRECTORY</replaceable>/funcs', 'makepoint'
+ LANGUAGE C STRICT;
+
+CREATE FUNCTION copytext(text) RETURNS text
+ AS '<replaceable>DIRECTORY</replaceable>/funcs', 'copytext'
+ LANGUAGE C STRICT;
+
+CREATE FUNCTION concat_text(text, text) RETURNS text
+ AS '<replaceable>DIRECTORY</replaceable>/funcs', 'concat_text'
+ LANGUAGE C STRICT;
+</programlisting>
+ </para>
+
+ <para>
+ Ici, <replaceable>DIRECTORY</replaceable> représente le répertoire
+ contenant le fichier de la bibliothèque partagée (par exemple le répertoire
+ du tutoriel de <productname>PostgreSQL</productname>, qui contient le code
+ des exemples utilisés dans cette section). (Un meilleur style aurait été
+ d'écrire seulement <literal>'funcs'</literal> dans la clause <literal>AS</literal>, après
+ avoir ajouté <replaceable>DIRECTORY</replaceable> au chemin de recherche.
+ Dans tous les cas, nous pouvons omettre l'extension spécifique au système
+ pour les bibliothèques partagées, communément <literal>.so</literal> ou
+ <literal>.sl</literal>.)
+ </para>
+
+ <para>
+ Remarquez que nous avons spécifié la fonction comme <quote>STRICT</quote>,
+ ce qui signifie que le système devra automatiquement supposer un résultat
+ NULL si n'importe quelle valeur d'entrée est NULL. Ainsi, nous évitons
+ d'avoir à vérifier l'existence d'entrées NULL dans le code de la fonction.
+ Sinon, nous aurions dû contrôler explicitement les valeurs NULL en testant
+ un pointeur NULL pour chaque argument passé par référence (pour les
+ arguments passés par valeur, nous n'aurions même aucun moyen de contrôle !).
+ </para>
+
+ <para>
+ Bien que cette convention d'appel soit simple à utiliser, elle n'est pas
+ très portable ; sur certaines architectures, il y a des problèmes
+ pour passer de cette manière des types de données plus petits
+ que <type>int</type>. De plus, il n'y a pas de moyen simple de renvoyer un
+ résultat NULL, ni de traiter des arguments NULL autrement qu'en rendant la
+ fonction strict. La convention version-1, présentée ci-après, permet de
+ surmonter ces objections.
+ </para>
+
+ </sect2>
+
+ <sect2>
+ <title>Conventions d'appel de la version 1</title>
+
+ <para>
+ La convention d'appel version-1 repose sur des macros pour supprimer la
+ plus grande partie de la complexité du passage d'arguments et de résultats.
+ La déclaration C d'une fonction en version-1 est toujours :
+
+<programlisting>Datum nom_fonction(PG_FUNCTION_ARGS)
+</programlisting>
+
+ De plus, la macro d'appel :
+<programlisting>PG_FUNCTION_INFO_V1(nom_fonction);
+</programlisting>
+ doit apparaître dans le même fichier source (par convention, elle est
+ écrite juste avant la fonction elle-même). Cette macro n'est pas nécessaire
+ pour les fonctions <literal>internal</literal> puisque <productname>PostgreSQL</productname>
+ assume que toutes les fonctions internes utilisent la convention version-1.
+ Elle est toutefois requise pour les fonctions chargées dynamiquement.
+ </para>
+
+ <para>
+ Dans une fonction version-1, chaque argument existant est traité par une
+ macro <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>
+ correspondant au type de donnée de l'argument et le résultat est renvoyé
+ par une macro
+ <function>PG_RETURN_<replaceable>xxx</replaceable>()</function>
+ correspondant au type renvoyé.
+ <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>
+ prend comme argument le nombre d'arguments de la fonction à parcourir, le
+ compteur commençant à 0.
+ <function>PG_RETURN_<replaceable>xxx</replaceable>()</function> prend comme
+ argument la valeur effective à renvoyer.
+ </para>
+
+ <para>
+ Voici la même fonction que précédemment, codée en style version-1
+
+<programlisting><![CDATA[#include "postgres.h"
+#include <string.h>
+#include "fmgr.h"
+
+/* par valeur */
+
+PG_FUNCTION_INFO_V1(add_one);
+
+Datum
+add_one(PG_FUNCTION_ARGS)
+{
+ int32 arg = PG_GETARG_INT32(0);
+
+ PG_RETURN_INT32(arg + 1);
+}
+
+/* par référence, longueur fixe */
+
+PG_FUNCTION_INFO_V1(add_one_float8);
+
+Datum
+add_one_float8(PG_FUNCTION_ARGS)
+{
+ /* La macro pour FLOAT8 cache sa nature de passage par référence. */
+ float8 arg = PG_GETARG_FLOAT8(0);
+
+ PG_RETURN_FLOAT8(arg + 1.0);
+}
+
+PG_FUNCTION_INFO_V1(makepoint);
+
+Datum
+makepoint(PG_FUNCTION_ARGS)
+{
+ /* Ici, la nature de passage par référence de Point n'est pas cachée. */
+ Point *pointx = PG_GETARG_POINT_P(0);
+ Point *pointy = PG_GETARG_POINT_P(1);
+ Point *new_point = (Point *) palloc(sizeof(Point));
+
+ new_point->x = pointx->x;
+ new_point->y = pointy->y;
+
+ PG_RETURN_POINT_P(new_point);
+}
+
+/* par référence, longueur variable */
+
+PG_FUNCTION_INFO_V1(copytext);
+
+Datum
+copytext(PG_FUNCTION_ARGS)
+{
+ text *t = PG_GETARG_TEXT_P(0);
+ /*
+ * VARSIZE est la longueur totale de la structure en octets.
+ */
+ text *new_t = (text *) palloc(VARSIZE(t));
+ SET_VARSIZE(new_t, VARSIZE(t));
+ /*
+ * VARDATA est un pointeur vers la région de données de la structure.
+ */
+ memcpy((void *) VARDATA(new_t), /* destination */
+ (void *) VARDATA(t), /* source */
+ VARSIZE(t) - VARHDRSZ); /* nombre d'octets */
+ PG_RETURN_TEXT_P(new_t);
+}
+
+PG_FUNCTION_INFO_V1(concat_text);
+
+Datum
+concat_text(PG_FUNCTION_ARGS)
+{
+ text *arg1 = PG_GETARG_TEXT_P(0);
+ text *arg2 = PG_GETARG_TEXT_P(1);
+ int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
+ text *new_text = (text *) palloc(new_text_size);
+
+ SET_VARSIZE(new_text, new_text_size);
+ memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
+ memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ),
+ VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
+ PG_RETURN_TEXT_P(new_text);
+}
+]]></programlisting>
+ </para>
+
+ <para>
+ Les commandes <command>CREATE FUNCTION</command> sont les mêmes que pour
+ leurs équivalents dans la version-0.
+ </para>
+
+ <para>
+ Au premier coup d'œil, les conventions de codage de la version-1 peuvent
+ sembler inutilement obscures. Pourtant, elles offrent nombre
+ d'améliorations car les macros peuvent cacher les détails superflus. Un
+ exemple est donné par la fonction <function>add_one_float8</function> où nous
+ n'avons plus besoin de prêter attention au fait que le type
+ <type>float8</type> est passé par référence. Un autre exemple de
+ simplification est donné par les macros pour les types à longueur variable
+ <literal>GETARG</literal> qui permettent un traitement plus efficace des valeurs
+ <quote>toasted</quote> (compressées ou hors-ligne).
+ </para>
+
+ <para>
+ Une des grandes améliorations dans les fonctions version-1 est le meilleur
+ traitement des entrées et des résultats NULL. La macro
+ <function>PG_ARGISNULL(<replaceable>n</replaceable>)</function> permet à une fonction
+ de tester si chaque entrée est NULL (évidemment, ceci n'est nécessaire que
+ pour les fonctions déclarées non <quote>STRICT</quote>). Comme avec les macros
+ <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>, les
+ arguments en entrée sont comptés à partir de zéro. Notez qu'on doit se
+ garder d'exécuter
+ <function>PG_GETARG_<replaceable>xxx</replaceable>()</function> jusqu'à
+ ce qu'on ait vérifié que l'argument n'est pas NULL. Pour renvoyer un
+ résultat NULL, exécutez la fonction
+ <function>PG_RETURN_NULL()</function> ; ceci convient aussi bien dans
+ les fonctions STRICT que non STRICT.
+ </para>
+
+ <para>
+ Les autres options proposées dans l'interface de nouveau style sont deux
+ variantes des macros
+ <function>PG_GETARG_<replaceable>xxx</replaceable>()</function>. La
+ première d'entre elles,
+ <function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>,
+ garantit le renvoi d'une copie de l'argument spécifié où nous pouvons
+ écrire en toute sécurité (les macros normales peuvent parfois renvoyer
+ un pointeur vers une valeur physiquement mise en mémoire dans une table qui
+ ne doit pas être modifiée. En utilisant les macros
+ <function>PG_GETARG_<replaceable>xxx</replaceable>_COPY()</function>, on
+ garantit l'écriture du résultat). La seconde variante se compose des macros
+ <function>PG_GETARG_<replaceable>xxx</replaceable>_SLICE()</function>
+ qui prennent trois arguments. Le premier est le nombre d'arguments de la
+ fonction (comme ci-dessus). Le second et le troisième sont le décalage et
+ la longueur du segment qui doit être renvoyé. Les décalages sont comptés à
+ partir de zéro et une longueur négative demande le renvoi du reste de la
+ valeur. Ces macros procurent un accès plus efficace à des parties de
+ valeurs à grande dimension dans le cas où elles ont un type de stockage en
+ mémoire <quote>external</quote> (le type de stockage d'une colonne peut
+ être spécifié en utilisant <literal>ALTER TABLE
+ <replaceable>nom_table</replaceable> ALTER COLUMN
+ <replaceable>nom_colonne</replaceable> SET STORAGE
+ <replaceable>typestockage</replaceable></literal>.
+ <replaceable>typestockage</replaceable> est un type parmi
+ <literal>plain</literal>, <literal>external</literal>, <literal>extended</literal> ou
+ <literal>main</literal>).
+ </para>
+
+ <para>
+ Enfin, les conventions d'appels de la version-1 rendent possible le renvoi
+ de résultats d'ensemble (<xref linkend="xfunc-c-return-set"/>),
+ l'implémentation de fonctions déclencheurs (<xref
+ linkend="triggers"/>) et d'opérateurs d'appel de langage procédural (<xref
+ linkend="plhandler"/>). Le code version-1 est aussi plus portable que celui
+ de version-0 car il ne contrevient pas aux restrictions du protocole
+ d'appel de fonction en C standard. Pour plus de détails, voir
+ <filename>src/backend/utils/fmgr/README</filename> dans les fichiers
+ sources de la distribution.
+ </para>
+
+ </sect2>
+
+ <sect2>
+ <title>Écriture du code</title>
+
+ <para>
+ Avant de nous intéresser à des sujets plus avancés, nous devons discuter de
+ quelques règles de codage des fonctions en langage C de
+ <productname>PostgreSQL</productname>. Bien qu'il soit possible de charger
+ des fonctions écrites dans des langages autre que le C dans
+ <productname>PostgreSQL</productname>, c'est habituellement difficile
+ (quand c'est possible) parce que les autres langages comme C++, FORTRAN ou
+ Pascal ne suivent pas fréquemment les mêmes conventions de nommage que le
+ C. C'est-à-dire que les autres langages ne passent pas les arguments et ne
+ renvoient pas les valeurs entre fonctions de la même manière. Pour cette
+ raison, nous supposerons que nos fonctions en langage C sont réellement
+ écrites en C.
+ </para>
+ <para>
+ Les règles de base pour l'écriture de fonctions C sont les suivantes :
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Utilisez <literal>pg_config
+ --includedir-server</literal><indexterm><primary>pg_config</primary><secondary>
+ avec des fonctions C définies par l'utilisateur</secondary></indexterm> pour découvrir où
+ sont installés les fichiers d'en-tête du serveur
+ <productname>PostgreSQL</productname> sur votre système (ou sur le système de vos
+ utilisateurs).
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Compilez et liez votre code de façon à ce qu'il soit chargé dynamiquement
+ dans <productname>PostgreSQL</productname>, ce qui requiert des informations
+ spéciales. Voir <xref linkend="dfunc"/> pour une explication détaillée
+ sur la façon de le faire pour votre système d'exploitation spécifique.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Rappelez-vous to définir un <quote>bloc magique</quote> pour votre
+ bibliothèque partagée, comme décrit dans <xref linkend="xfunc-c-dynload"/>.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Quand vous allouez de la mémoire, utilisez les fonctions
+ <productname>PostgreSQL</productname>
+ <function>palloc</function><indexterm><primary>palloc</primary></indexterm> et
+ <function>pfree</function><indexterm><primary>pfree</primary></indexterm>
+ au lieu des fonctions correspondantes <function>malloc</function> et
+ <function>free</function> de la bibliothèque C. La mémoire allouée par
+ <function>palloc</function> sera libérée automatiquement à la fin de
+ chaque transaction, empêchant des débordements de mémoire.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Remettez toujours à zéro les octets de vos structures en utilisant
+ <function>memset</function>. Sinon, il est difficile de supporter les
+ index ou les jointures de découpage car vous devez retenir seulement
+ les bits significatifs de votre structure de donnée pour calculer un
+ découpage. Même si vous initialisez tous les champs de votre structure,
+ il peut y avoir des remplissages d'alignement (trous dans la structure)
+ pouvant contenir des valeurs parasites.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ La plupart des types internes <productname>PostgreSQL</productname>
+ sont déclarés dans <filename>postgres.h</filename> alors que les
+ interfaces de gestion des fonctions
+ (<symbol>PG_FUNCTION_ARGS</symbol>, etc.) sont dans
+ <filename>fmgr.h</filename>. Du coup, vous aurez besoin d'inclure au
+ moins ces deux fichiers. Pour des raisons de portabilité, il vaut
+ mieux inclure <filename>postgres.h</filename> <emphasis>en premier</emphasis>
+ avant tout autre fichier d'en-tête système ou utilisateur. En incluant
+ <filename>postgres.h</filename>, il incluera également
+ <filename>elog.h</filename> et <filename>palloc.h</filename> pour vous.
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+ Les noms de symboles définis dans les objets ne doivent pas entrer en
+ conflit entre eux ou avec les symboles définis dans les exécutables du
+ serveur <productname>PostgreSQL</productname>. Vous aurez à renommer vos
+ fonctions ou variables si vous recevez un message d'erreur à cet effet.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+
+&dfunc;
+
+<sect2 id="xfunc-c-pgxs">
+ <title>Infrastructure de construction d'extensions</title>
+
+ <indexterm zone="xfunc-c-pgxs">
+ <primary>pgxs</primary>
+ </indexterm>
+
+ <para>
+ Si vous pensez distribuer vos modules d'extension
+ <productname>PostgreSQL</productname>, configurer un système de construction
+ portable peut être assez compliqué. Du coup, l'installation de
+ <productname>PostgreSQL</productname> fournit une infrastructure de construction pour
+ les extensions, appelée <acronym>PGXS</acronym>, pour que les modules
+ d'extension simples puissent être construit simplement avec un serveur
+ déjà installé. Notez que cette infrastructure n'a pas pour but d'être un
+ ensemble de travail universel pouvant être utilisé pour construire tous
+ les logiciels s'interfaçant avec <productname>PostgreSQL</productname> ; il
+ automatise simplement les règles de construction communes pour les modules
+ simples d'extension du serveur. Pour des paquetages plus complexes, vous
+ aurez besoin d'écrire votre propre système de construction.
+ </para>
+
+ <para>
+ Pour utiliser l'infrastructure de votre extension, vous devez écrire un
+ simple fichier makefile. Dans ce fichier, vous devez configurer quelques
+ variables et inclure enfin le makefile global <acronym>PGXS</acronym>.
+ Voici un exemple qui construit un module d'extension nommé
+ <literal>isbn_issn</literal> consistant en une bibliothèque partagée, un
+ script SQL et un fichier texte de documentation :
+<programlisting>MODULES = isbn_issn
+DATA_built = isbn_issn.sql
+DOCS = README.isbn_issn
+
+PG_CONFIG := pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+</programlisting>
+ Les trois dernières lignes devraient toujours être identiques. Plus tôt
+ dans le fichier, vous affectez des variables ou ajoutez vos propres règles
+ pour <application>make</application>.
+ </para>
+
+ <para>
+ Les variables suivantes peuvent être configurées :
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>MODULES</varname></term>
+ <listitem>
+ <para>
+ liste des objets partagés à construire à partir du fichier source
+ de même base (ne pas inclure le suffixe dans cette liste)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>DATA</varname></term>
+ <listitem>
+ <para>
+ fichiers spécifiques à installer dans
+ <literal><replaceable>prefix</replaceable>/share/contrib</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>DATA_built</varname></term>
+ <listitem>
+ <para>
+ fichiers spécifiques à installer dans
+ <literal><replaceable>prefix</replaceable>/share/contrib</literal>
+ qui ont besoin d'être construit au début
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>DOCS</varname></term>
+ <listitem>
+ <para>
+ fichiers spécifiques à installer dans
+ <literal><replaceable>prefix</replaceable>/doc/contrib</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SCRIPTS</varname></term>
+ <listitem>
+ <para>
+ fichiers script (pas des binaires) à installer dans
+ <literal><replaceable>prefix</replaceable>/bin</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SCRIPTS_built</varname></term>
+ <listitem>
+ <para>
+ fichiers script (pas des binaires) à installer dans
+ <literal><replaceable>prefix</replaceable>/bin</literal>
+ qui ont besoin d'être construit au début
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>REGRESS</varname></term>
+ <listitem>
+ <para>
+ liste des cas de tests de regression (sans suffixe), voir ci-dessous
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ ou au moins un parmi ces deux-là :
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>PROGRAM</varname></term>
+ <listitem>
+ <para>
+ un programme binaire à construire (liste des fichiers objets dans
+ <varname>OBJS</varname>)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>MODULE_big</varname></term>
+ <listitem>
+ <para>
+ un objet partagé à construire (liste des fichiers objets dans
+ <varname>OBJS</varname>)
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Ce qui suit peut être configuré :
+
+ <variablelist>
+
+ <varlistentry>
+ <term><varname>EXTRA_CLEAN</varname></term>
+ <listitem>
+ <para>
+ fichiers supplémentaires à supprimer dans <literal>make
+ clean</literal>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PG_CPPFLAGS</varname></term>
+ <listitem>
+ <para>
+ sera ajouté à <varname>CPPFLAGS</varname>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PG_LIBS</varname></term>
+ <listitem>
+ <para>
+ sera ajouté à la ligne de liens <varname>PROGRAM</varname>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SHLIB_LINK</varname></term>
+ <listitem>
+ <para>
+ sera ajouté à la ligne de lien <varname>MODULE_big</varname>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>PG_CONFIG</varname></term>
+ <listitem>
+ <para>
+ chemin vers le programme <application>pg_config</application> indiquant
+ l'installation de <productname>PostgreSQL</productname> qui sert à la
+ construction (généralement <literal>pg_config</literal> pour utiliser
+ le premier programme trouvé dans la variable d'environnement
+ <varname>PATH</varname>)
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+
+ <para>
+ Nommez ce fichier <literal>Makefile</literal> et placez le dans le répertoire
+ qui contient votre extension. Ensuite, vous pouvez lancer la compilation
+ avec <literal>make</literal>, et plus tard <literal>make
+ install</literal> pour installer votre module. Par défaut, l'extension est
+ compilée et installée pour l'installation de
+ <productname>PostgreSQL</productname> qui correspond au premier programme
+ <command>pg_config</command> trouvée dans votre chemin. Vous pouvez utiliser
+ utiliser une autre installation en configurant <varname>PG_CONFIG</varname>
+ pour qu'il pointe vers le bon <command>pg_config</command>, soit dans le
+ fichier Makefile soit sur la ligne de commande de <literal>make</literal>.
+ </para>
+
+ <caution>
+ <para>
+ Modifier <varname>PG_CONFIG</varname> fonctionne seulement en construisant
+ à partir d'une installation <productname>PostgreSQL</productname> 8.3 ou
+ ultérieure. Les anciennes versions ne l'utilisent pas ; vous devez
+ donc modifier votre variable <varname>PATH</varname> pour sélectionner
+ la bonne installation.
+ </para>
+ </caution>
+
+ <para>
+ Les scripts listés dans la variable <varname>REGRESS</varname> sont utilisés
+ pour les tests de régression de votre module, tout comme <literal>make
+ installcheck</literal> est utilisé pour votre serveur
+ <productname>PostgreSQL</productname>. Pour que ceci fonctionne, vous avez
+ besoin d'avoir un sous-répertoire nommé <literal>sql/</literal> dans le
+ répertoire de votre extension, à l'intérieur duquel vous placez un fichier
+ pour chaque groupe de tests que vous voulez exécuter. Les fichiers doivent
+ avoir l'extension <literal>.sql</literal>, qui ne seront pas inclus dans la
+ liste <varname>REGRESS</varname> dans le Makefile. Pour chaque test, il doit
+ exister un fichier contenant le résultat attendu dans un sous-répertoire nommé
+ <literal>expected/</literal>, avec l'extension <literal>.out</literal>. Les
+ tests sont exécutés via <literal>make installcheck</literal>, et le résultat sera
+ comparé aux fichiers attendus. Les différences seront écrites dans le fichier
+ <literal>regression.diffs</literal> dans un format <command>diff -c</command>.
+ Notez qu'essayer d'exécuter un test à qui il manque le fichier sera rapporté
+ comme un <quote>problème</quote>, donc assurez-vous que vous avez bien tous les
+ fichiers attendus.
+ </para>
+
+ <tip>
+ <para>
+ La façon la plus simple de créer les fichiers attendus est de créer des
+ fichiers vides, puis de vérifier avec précaution les fichiers résultants
+ après un test d'exécution (à trouver dans un répertoire
+ <literal>results/</literal>), et les copier dans
+ <literal>expected/</literal> s'ils correspondent à votre souhait suite aux
+ tests.
+ </para>
+ </tip>
+ </sect2>
+
+
+ <sect2>
+ <title>Arguments de type composite</title>
+
+ <para>
+ Les types composites n'ont pas une organisation fixe comme les structures
+ en C. Des instances d'un type composite peuvent contenir des champs NULL.
+ De plus, les types composites faisant partie d'une hiérarchie d'héritage
+ peuvent avoir des champs différents des autres membres de la même
+ hiérarchie. En conséquence, <productname>PostgreSQL</productname> propose
+ une interface de fonction pour accéder depuis le C aux champs des types
+ composites.
+ </para>
+
+ <para>
+ Supposons que nous voulions écrire une fonction pour répondre à la requête :
+<programlisting>SELECT nom, c_surpaye(emp, 1500) AS surpaye
+ FROM emp
+ WHERE nom = 'Bill' OR nom = 'Sam';
+</programlisting>
+
+ En utilisant les conventions d'appel de la version 0, nous pouvons définir
+ <function>c_surpaye</function> comme :
+
+<programlisting><![CDATA[#include "postgres.h"
+#include "executor/executor.h" /* pour GetAttributeByName() */
+
+bool
+c_surpaye(HeapTupleHeader *t, /* la ligne courante d'emp */
+ int32 limite)
+{
+ bool isNULL;
+ int32 salaire;
+
+ salaire = DatumGetInt32(GetAttributeByName(t, "salaire", &isNULL));
+ if (isNULL)
+ return false;
+ return salaire > limite;
+}
+]]></programlisting>
+
+ Dans le codage version-1, le code ci-dessus devient :
+
+<programlisting><![CDATA[#include "postgres.h"
+#include "executor/executor.h" /* pour GetAttributeByName() */
+
+PG_FUNCTION_INFO_V1(c_overpaid);
+
+Datum
+c_overpaid(PG_FUNCTION_ARGS)
+{
+ HeapTupleHeader *t = (HeapTupleHeader *) PG_GETARG_HEAPTUPLEHEADER(0);
+ int32 limite = PG_GETARG_INT32(1);
+ bool isNULL;
+ Datum salaire;
+
+ salaire = GetAttributeByName(t, "salaire", &isNULL);
+ if (isNULL)
+ PG_RETURN_BOOL(false);
+ /* Autrement, nous pourrions préférer de lancer PG_RETURN_NULL() pour un
+ salaire NULL.
+ */
+
+ PG_RETURN_BOOL(DatumGetInt32(salaire) > limite);
+}
+]]></programlisting>
+ </para>
+
+ <para>
+ <function>GetAttributeByName</function> est la fonction système
+ <productname>PostgreSQL</productname> qui renvoie les attributs depuis une
+ colonne spécifiée. Elle a trois arguments : l'argument de type
+ <type>HeapTupleHeader</type> passé à la fonction, le nom de l'attribut
+ recherché et un paramètre de retour qui indique si l'attribut est NULL.
+ <function>GetAttributeByName</function> renvoie une valeur de type
+ <type>Datum</type> que vous pouvez convertir dans un type voulu en
+ utilisant la macro appropriée
+ <function>DatumGet<replaceable>XXX</replaceable>()</function>. Notez que
+ la valeur de retour est insignifiante si le commutateur NULL est
+ positionné ; il faut toujours vérifier le commutateur NULL avant de commencer
+ à faire quelque chose avec le résultat.
+ </para>
+
+ <para>
+ Il y a aussi <function>GetAttributeByNum</function>, qui sélectionne
+ l'attribut cible par le numéro de colonne au lieu de son nom.
+ </para>
+
+ <para>
+ La commande suivante déclare la fonction <function>c_surpaye</function>
+ en SQL :
+
+<programlisting>CREATE FUNCTION c_surpaye(emp, integer) RETURNS boolean
+ AS '<replaceable>DIRECTORY</replaceable>/funcs', 'c_surpaye'
+ LANGUAGE C STRICT;
+</programlisting>
+
+ Notez que nous avons utilisé <literal>STRICT</literal> pour que nous n'ayons pas à
+ vérifier si les arguments en entrée sont NULL.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Renvoi de lignes (types composites)</title>
+
+ <para>
+ Pour renvoyer une ligne ou une valeur de type composite à partir d'une
+ fonction en langage C, vous pouvez utiliser une API spéciale qui fournit
+ les macros et les fonctions dissimulant en grande partie la complexité
+ liée à la construction de types de données composites. Pour utiliser cette
+ API, le fichier source doit inclure :
+ <programlisting>#include "funcapi.h"</programlisting>
+ </para>
+
+ <para>
+ Il existe deux façons de construire une valeur de données composites
+ (autrement dit un <quote>tuple</quote>) : vous pouvez le construire à
+ partir d'un tableau de valeurs Datum ou à partir d'un tableau de
+ chaînes C qui peuvent passer dans les fonctions de conversion des types
+ de données du tuple. Quelque soit le cas, vous avez d'abord besoin
+ d'obtenir et de construire un descripteur <structname>TupleDesc</structname> pour
+ la structure du tuple. En travaillant avec des Datums, vous passez le
+ <structname>TupleDesc</structname> à <function>BlessTupleDesc</function>, puis vous appelez
+ <function>heap_form_tuple</function> pour chaque ligne. En travaillant avec des
+ chaînes C, vous passez <structname>TupleDesc</structname> à
+ <function>TupleDescGetAttInMetadata</function>, puis vous appelez
+ <function>BuildTupleFromCStrings</function> pour chaque ligne. Dans le cas d'une
+ fonction renvoyant un ensemble de tuple, les étapes de configuration
+ peuvent toutes être entreprises une fois lors du premier appel à la
+ fonction.
+ </para>
+
+ <para>
+ Plusieurs fonctions d'aide sont disponibles pour configurer le
+ <structname>TupleDesc</structname> requis. La façon recommandée de le faire dans la
+ plupart des fonctions renvoyant des valeurs composites est d'appeler :
+<programlisting>TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo,
+ Oid *resultTypeId,
+ TupleDesc *resultTupleDesc)
+</programlisting>
+ en passant la même structure <literal>fcinfo</literal> que celle passée à la
+ fonction appelante (ceci requiert bien sûr que vous utilisez les
+ conventions d'appel version-1). <varname>resultTypeId</varname> peut être
+ spécifié comme <literal>NULL</literal> ou comme l'adresse d'une variable locale
+ pour recevoir l'OID du type de résultat de la fonction.
+ <varname>resultTupleDesc</varname> devrait être l'adresse d'une variable
+ <structname>TupleDesc</structname> locale. Vérifiez que le résultat est
+ <literal>TYPEFUNC_COMPOSITE</literal> ; dans ce cas,
+ <varname>resultTupleDesc</varname> a été rempli avec le
+ <structname>TupleDesc</structname> requis (si ce n'est pas le cas, vous pouvez
+ rapporter une erreur pour une <quote>fonction renvoyant un enregistrement
+ appelé dans un contexte qui ne peut pas accepter ce type
+ enregistrement</quote>).
+ </para>
+
+ <tip>
+ <para>
+ <function>get_call_result_type</function> peut résoudre le vrai type du
+ résultat d'une fonction polymorphique ; donc, il est utile pour les
+ fonctions qui renvoient des résultats scalaires polymorphiques, pas
+ seulement les fonctions qui renvoient des types composites. Le résultat
+ <varname>resultTypeId</varname> est principalement utile pour les fonctions
+ renvoyant des scalaires polymorphiques.
+ </para>
+ </tip>
+
+ <note>
+ <para>
+ <function>get_call_result_type</function> a une fonction cousine
+ <function>get_expr_result_type</function>, qui peut être utilisée pour résoudre
+ le tupe attendu en sortie en un appel de fonction représenté par
+ un arbre d'expressions. Ceci peut être utilisé pour tenter de déterminer
+ le type de résultat sans entrer dans la fonction elle-même. Il existe
+ aussi <function>get_func_result_type</function>, qui peut seulement être utilisée
+ quand l'OID de la fonction est disponible. Néanmoins, ces fonctions ne
+ sont pas capables de gérer les fonctions déclarées renvoyer des
+ enregistrements (<structname>record</structname>).
+ <function>get_func_result_type</function> ne peut pas résoudre les types
+ polymorphiques, donc vous devriez utiliser de préférence
+ <function>get_call_result_type</function>.
+ </para>
+ </note>
+
+ <para>
+ Les fonctions anciennes, et maintenant obsolètes, qui permettent d'obtenir des
+ <structname>TupleDesc</structname> sont :
+<programlisting>TupleDesc RelationNameGetTupleDesc(const char *relname)
+</programlisting>
+ pour obtenir un <structname>TupleDesc</structname> pour le type de ligne d'une
+ relation nommée ou :
+<programlisting>TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
+</programlisting>
+ pour obtenir une <structname>TupleDesc</structname> basée sur l'OID d'un type. Ceci
+ peut être utilisé pour obtenir un <structname>TupleDesc</structname> soit pour un
+ type de base, soit pour un type composite. Néanmoins, cela ne fonctionnera
+ pas pour une fonction qui renvoie <structname>record</structname> et cela ne résoudra
+ pas les types polymorphiques.
+ </para>
+
+ <para>
+ Une fois que vous avez un <structname>TupleDesc</structname>, appelez :
+<programlisting>TupleDesc BlessTupleDesc(TupleDesc tupdesc)
+</programlisting>
+ si vous pensez travailler avec des Datums ou :
+<programlisting>AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
+</programlisting>
+ si vous pensez travailler avec des chaînes C. Si vous écrivez une
+ fonction renvoyant un ensemble, vous pouvez sauvegarder les résultats
+ de ces fonctions dans la structure dans le
+ <structname>FuncCallContext</structname> — utilisez le champ
+ <structfield>tuple_desc</structfield> ou <structfield>attinmeta</structfield> respectivement.
+ </para>
+
+ <para>
+ Lorsque vous fonctionnez avec des Datums, utilisez :
+<programlisting>HeapTuple heap_form_tuple(TupleDesc tupdesc, Datum *values, bool *isnull)
+</programlisting>
+ pour construire une donnée utilisateur <structname>HeapTuple</structname> indiquée
+ dans le format Datum.
+ </para>
+
+ <para>
+ Lorsque vous travaillez avec des chaînes C, utilisez :
+<programlisting>HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
+</programlisting>
+ pour construire une donnée utilisateur <structname>HeapTuple</structname> indiquée
+ dans le format des chaînes C. <literal>values</literal> est un tableau de
+ chaîne C, une pour chaque attribut de la ligne renvoyée. Chaque chaîne
+ C doit être de la forme attendue par la fonction d'entrée du type de
+ donnée de l'attribut. Afin de renvoyer une valeur NULL pour un des
+ attributs, le pointeur correspondant dans le tableau de valeurs
+ (<parameter>values</parameter>) doit être fixé à <symbol>NULL</symbol>. Cette fonction
+ demandera à être appelée pour chaque ligne que vous renvoyez.
+ </para>
+
+ <para>
+ Une fois que vous avez construit un tuple devant être renvoyé par votre
+ fonction, vous devez le convertir en type <type>Datum</type>. Utilisez :
+<programlisting>HeapTupleGetDatum(HeapTuple tuple)
+</programlisting>
+ pour convertir un type <type>HeapTuple</type> en un Datum valide.
+ Ce <type>Datum</type> peut être renvoyé directement si vous envisagez de
+ renvoyer juste une simple ligne ou bien il peut être utilisé pour renvoyer
+ la valeur courante dans une fonction renvoyant un ensemble.
+ </para>
+
+ <para>
+ Un exemple figure dans la section suivante.
+ </para>
+
+ </sect2>
+
+ <sect2 id="xfunc-c-return-set">
+ <title>Renvoi d'ensembles</title>
+
+ <para>
+ Il existe aussi une API spéciale procurant le moyen de renvoyer des
+ ensembles (lignes multiples) depuis une fonction en langage C. Une fonction
+ renvoyant un ensemble doit suivre les conventions d'appel de la version-1.
+ Aussi, les fichiers source doivent inclure l'en-tête
+ <filename>funcapi.h</filename>, comme ci-dessus.
+ </para>
+
+ <para>
+ Une fonction renvoyant un ensemble (<acronym>SRF</acronym> : <quote>set
+ returning function</quote>) est appelée une fois pour chaque élément
+ qu'elle renvoie. La <acronym>SRF</acronym> doit donc sauvegarder suffisamment
+ l'état pour se rappeler ce qu'elle était en train de faire et renvoyer le
+ prochain élément à chaque appel. La structure
+ <structname>FuncCallContext</structname> est offerte pour assister le contrôle de ce
+ processus. À l'intérieur d'une fonction,
+ <literal>fcinfo->flinfo->fn_extra</literal> est utilisée pour conserver un
+ pointeur vers <structname>FuncCallContext</structname> au cours des appels
+ successifs.
+
+<programlisting>typedef struct
+{
+ /*
+ * Number of times we've been called before
+ *
+ * call_cntr is initialized to 0 for you by SRF_FIRSTCALL_INIT(), and
+ * incremented for you every time SRF_RETURN_NEXT() is called.
+ */
+ uint32 call_cntr;
+
+ /*
+ * OPTIONAL maximum number of calls
+ *
+ * max_calls is here for convenience only and setting it is optional.
+ * If not set, you must provide alternative means to know when the
+ * function is done.
+ */
+ uint32 max_calls;
+
+ /*
+ * OPTIONAL pointer to result slot
+ *
+ * This is obsolete and only present for backwards compatibility, viz,
+ * user-defined SRFs that use the deprecated TupleDescGetSlot().
+ */
+ TupleTableSlot *slot;
+
+ /*
+ * OPTIONAL pointer to miscellaneous user-provided context information
+ *
+ * user_fctx is for use as a pointer to your own data to retain
+ * arbitrary context information between calls of your function.
+ */
+ void *user_fctx;
+
+ /*
+ * OPTIONAL pointer to struct containing attribute type input metadata
+ *
+ * attinmeta is for use when returning tuples (i.e., composite data types)
+ * and is not used when returning base data types. It is only needed
+ * if you intend to use BuildTupleFromCStrings() to create the return
+ * tuple.
+ */
+ AttInMetadata *attinmeta;
+
+ /*
+ * memory context used for structures that must live for multiple calls
+ *
+ * multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used
+ * by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory
+ * context for any memory that is to be reused across multiple calls
+ * of the SRF.
+ */
+ MemoryContext multi_call_memory_ctx;
+
+ /*
+ * OPTIONAL pointer to struct containing tuple description
+ *
+ * tuple_desc is for use when returning tuples (i.e. composite data types)
+ * and is only needed if you are going to build the tuples with
+ * heap_form_tuple() rather than with BuildTupleFromCStrings(). Note that
+ * the TupleDesc pointer stored here should usually have been run through
+ * BlessTupleDesc() first.
+ */
+ TupleDesc tuple_desc;
+
+} FuncCallContext;
+</programlisting>
+ </para>
+
+ <para>
+ Une <acronym>SRF</acronym> utilise plusieurs fonctions et macros qui manipulent
+ automatiquement la structure <structname>FuncCallContext</structname> (et s'attendent
+ à la trouver via <literal>fn_extra</literal>). Utilisez :
+<programlisting>SRF_IS_FIRSTCALL()
+</programlisting>
+ pour déterminer si votre fonction est appelée pour la première fois. Au
+ premier appel, utilisez :
+<programlisting>SRF_FIRSTCALL_INIT()
+</programlisting>
+ pour initialiser la structure <structname>FuncCallContext</structname>. À chaque
+ appel de fonction, y compris le premier, utilisez :
+<programlisting>SRF_PERCALL_SETUP()
+</programlisting>
+ pour une mise à jour correcte en vue de l'utilisation de
+ <structname>FuncCallContext</structname> et pour nettoyer toutes les données
+ renvoyées précédemment et conservées depuis le dernier passage de la
+ fonction.
+ </para>
+
+ <para>
+ Si votre fonction a des données à renvoyer, utilisez :
+<programlisting>SRF_RETURN_NEXT(funcctx, result)
+</programlisting>
+ pour les renvoyer à l'appelant. (<literal>result</literal> doit être de type
+ <type>Datum</type>, soit une valeur simple, soit un tuple préparé comme décrit
+ ci-dessus.) Enfin, quand votre fonction a fini de renvoyer des données,
+ utilisez :
+<programlisting>SRF_RETURN_DONE(funcctx)
+</programlisting>
+ pour nettoyer et terminer la <acronym>SRF</acronym>.
+ </para>
+
+ <para>
+ Lors de l'appel de la <acronym>SRF</acronym>, le contexte mémoire courant est un
+ contexte transitoire qui est effacé entre les appels. Cela signifie que
+ vous n'avez pas besoin d'appeler <function>pfree</function> sur tout ce que vous
+ avez alloué en utilisant <function>palloc</function> ; ce sera supprimé de
+ toute façon. Toutefois, si vous voulez allouer des structures de données
+ devant persister tout au long des appels, vous avez besoin de les conserver
+ quelque part. Le contexte mémoire référencé par
+ <structfield>multi_call_memory_ctx</structfield> est un endroit approprié pour toute
+ donnée devant survivre jusqu'à l'achèvement de la fonction <acronym>SRF</acronym>.
+ Dans la plupart des cas, cela signifie que vous devrez basculer vers
+ <structfield>multi_call_memory_ctx</structfield> au moment de la préparation du
+ premier appel.
+ </para>
+ <para>
+ Voici un exemple complet de pseudo-code :
+<programlisting>Datum
+my_set_returning_function(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ Datum result;
+ MemoryContext oldcontext;
+ <replaceable>further declarations as needed</replaceable>
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ funcctx = SRF_FIRSTCALL_INIT();
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ /* One-time setup code appears here: */
+ <replaceable>user code</replaceable>
+ <replaceable>if returning composite</replaceable>
+ <replaceable>build TupleDesc, and perhaps AttInMetadata</replaceable>
+ <replaceable>endif returning composite</replaceable>
+ <replaceable>user code</replaceable>
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /* Each-time setup code appears here: */
+ <replaceable>user code</replaceable>
+ funcctx = SRF_PERCALL_SETUP();
+ <replaceable>user code</replaceable>
+
+ /* this is just one way we might test whether we are done: */
+ if (funcctx->call_cntr < funcctx->max_calls)
+ {
+ /* Here we want to return another item: */
+ <replaceable>user code</replaceable>
+ <replaceable>obtain result Datum</replaceable>
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ else
+ {
+ /* Here we are done returning items and just need to clean up: */
+ <replaceable>user code</replaceable>
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+</programlisting>
+ </para>
+
+ <para>
+ Et voici un exemple complet d'une simple <acronym>SRF</acronym> retournant un
+ type composite :
+ <programlisting><![CDATA[PG_FUNCTION_INFO_V1(retcomposite);
+
+Datum
+retcomposite(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ int call_cntr;
+ int max_calls;
+ TupleDesc tupdesc;
+ AttInMetadata *attinmeta;
+
+ /* stuff done only on the first call of the function */
+ if (SRF_IS_FIRSTCALL())
+ {
+ MemoryContext oldcontext;
+
+ /* create a function context for cross-call persistence */
+ funcctx = SRF_FIRSTCALL_INIT();
+
+ /* switch to memory context appropriate for multiple function calls */
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ /* total number of tuples to be returned */
+ funcctx->max_calls = PG_GETARG_UINT32(0);
+
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("function returning record called in context "
+ "that cannot accept type record")));
+
+ /*
+ * generate attribute metadata needed later to produce tuples from raw
+ * C strings
+ */
+ attinmeta = TupleDescGetAttInMetadata(tupdesc);
+ funcctx->attinmeta = attinmeta;
+
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /* stuff done on every call of the function */
+ funcctx = SRF_PERCALL_SETUP();
+
+ call_cntr = funcctx->call_cntr;
+ max_calls = funcctx->max_calls;
+ attinmeta = funcctx->attinmeta;
+
+ if (call_cntr < max_calls) /* do when there is more left to send */
+ {
+ char **values;
+ HeapTuple tuple;
+ Datum result;
+
+ /*
+ * Prepare a values array for building the returned tuple.
+ * This should be an array of C strings which will
+ * be processed later by the type input functions.
+ */
+ values = (char **) palloc(3 * sizeof(char *));
+ values[0] = (char *) palloc(16 * sizeof(char));
+ values[1] = (char *) palloc(16 * sizeof(char));
+ values[2] = (char *) palloc(16 * sizeof(char));
+
+ snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
+ snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
+ snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));
+
+ /* build a tuple */
+ tuple = BuildTupleFromCStrings(attinmeta, values);
+
+ /* make the tuple into a datum */
+ result = HeapTupleGetDatum(tuple);
+
+ /* clean up (this is not really necessary) */
+ pfree(values[0]);
+ pfree(values[1]);
+ pfree(values[2]);
+ pfree(values);
+
+ SRF_RETURN_NEXT(funcctx, result);
+ }
+ else /* do when there is no more left */
+ {
+ SRF_RETURN_DONE(funcctx);
+ }
+}
+]]></programlisting>
+
+ Voici une façon de déclarer cette fonction en SQL :
+
+<programlisting>CREATE TYPE __retcomposite AS (f1 integer, f2 integer, f3 integer);
+
+CREATE OR REPLACE FUNCTION retcomposite(integer, integer)
+ RETURNS SETOF __retcomposite
+ AS '<replaceable>filename</replaceable>', 'retcomposite'
+ LANGUAGE C IMMUTABLE STRICT;
+</programlisting>
+ Une façon différente de le faire est d'utiliser des paramètres OUT :
+<programlisting>CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
+ OUT f1 integer, OUT f2 integer, OUT f3 integer)
+ RETURNS SETOF record
+ AS '<replaceable>filename</replaceable>', 'retcomposite'
+ LANGUAGE C IMMUTABLE STRICT;
+</programlisting>
+ Notez que dans cette méthode le type en sortie de la fonction est du type
+ <structname>record</structname> anonyme.
+ </para>
+
+ <para>
+ Le répertoire <filename>contrib/tablefunc</filename> situé dans les fichiers source
+ de la distribution contient d'autres exemples de fonctions renvoyant des
+ ensembles.</para>
+ </sect2>
+
+ <sect2>
+ <title>Arguments polymorphes et types renvoyés</title>
+
+ <para>
+ Les fonctions en langage C peuvent être déclarées pour accepter et renvoyer
+ les types <quote>polymorphes</quote> <type>anyelement</type>,
+ <type>anyarray</type>, <type>anynonarray</type> et <type>anyenum</type>. Voir la <xref linkend="extend-types-polymorphic"/> pour une
+ explication plus détaillée des fonctions polymorphes. Si les types des
+ arguments ou du renvoi de la fonction sont définis comme polymorphes,
+ l'auteur de la fonction ne peut pas savoir à l'avance quel type de données
+ sera appelé ou bien quel type doit être renvoyé. Il y a deux routines
+ offertes par <filename>fmgr.h</filename> qui permettent à une fonction en
+ version-1 de découvrir les types de données effectifs de ses arguments et
+ le type qu'elle doit renvoyer. Ces routines s'appellent
+ <literal>get_fn_expr_rettype(FmgrInfo *flinfo)</literal> et
+ <literal>get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)</literal>. Elles
+ renvoient l'OID du type du résultat ou de l'argument ou InvalidOID si
+ l'information n'est pas disponible. L'accès à la structure
+ <literal>flinfo</literal> se fait normalement avec
+ <literal>fcinfo->flinfo</literal>. Le paramètre <literal>argnum</literal> est basé à
+ partir de zéro. <function>get_call_result_type</function> peut aussi être utilisé
+ comme alternative à <function>get_fn_expr_rettype</function>.
+ </para>
+
+ <para>
+ Par exemple, supposons que nous voulions écrire une fonction qui accepte un
+ argument de n'importe quel type et qui renvoie un tableau uni-dimensionnel
+ de ce type :
+
+<programlisting>PG_FUNCTION_INFO_V1(make_array);
+Datum
+make_array(PG_FUNCTION_ARGS)
+{
+ ArrayType *result;
+ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+ Datum element;
+ bool isnull;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int ndims;
+ int dims[MAXDIM];
+ int lbs[MAXDIM];
+
+ if (!OidIsValid(element_type))
+ elog(ERROR, "could not determine data type of input");
+
+ /* get the provided element, being careful in case it's NULL */
+ isnull = PG_ARGISNULL(0);
+ if (isnull)
+ element = (Datum) 0;
+ else
+ element = PG_GETARG_DATUM(0);
+
+ /* we have one dimension */
+ ndims = 1;
+ /* and one element */
+ dims[0] = 1;
+ /* and lower bound is 1 */
+ lbs[0] = 1;
+
+ /* get required info about the element type */
+ get_typlenbyvalalign(element_type, &typlen, &typbyval,
+&typalign);
+
+ /* now build the array */
+ result = construct_md_array(&element, &isnull, ndims, dims, lbs,
+ element_type, typlen, typbyval, typalign);
+
+ PG_RETURN_ARRAYTYPE_P(result);
+}
+</programlisting>
+ </para>
+
+ <para>
+ La commande suivante déclare la fonction <function>make_array</function> en
+ SQL :
+
+<programlisting>CREATE FUNCTION make_array(anyelement)
+ RETURNS anyarray
+ AS '<replaceable>DIRECTORY</replaceable>/funcs', 'make_array'
+ LANGUAGE 'C' IMMUTABLE;
+</programlisting>
+
+ Notez l'utilisation de STRICT ; ceci est primordial car le code ne se
+ préoccupe pas de tester une entrée NULL.
+ </para>
+
+ <para>
+ Il existe une variante du polymorphisme qui est seulement disponible pour
+ les fonctions en langage C : elles peuvent être déclarées prendre des
+ paramètres de type <literal>"any"</literal>. (Notez que ce nom de type doit
+ être placé entre des guillemets doubles car il s'agit d'un mot SQL réservé.)
+ Ceci fonctionne comme <type>anyelement</type> sauf qu'il ne contraint pas
+ les différents arguments <literal>"any"</literal> à être du même type, pas
+ plus qu'ils n'aident à déterminer le type de résultat de la fonction. Une
+ fonction en langage C peut aussi déclarer son paramètre final ainsi :
+ <literal>VARIADIC "any"</literal>. Cela correspondra à un ou plusieurs
+ arguments réels de tout type (pas nécessairement le même type). Ces
+ arguments ne seront <emphasis>pas</emphasis> placés dans un tableau
+ comme c'est le cas pour les fonctions variadic normales ; ils seront
+ passés séparément à la fonction. La macro <function>PG_NARGS()</function>
+ et les méthodes décrites ci-dessus doivent être utilisées pour déterminer
+ le nombre d'arguments réels et leur type lors de l'utilisation de cette
+ fonctionnalité.
+ </para>
+ </sect2>
+
+ <sect2>
+ <title>Mémoire partagée et LWLocks</title>
+
+ <para>
+ Les modules peuvent réserver des LWLocks et allouer de la mémoire partagée
+ au lancement du serveur. La bibliothèque partagée du module doit être
+ préchargée en l'ajoutant <xref
+ linkend="guc-shared-preload-libraries"/><indexterm><primary>shared-preload-libraries</primary></indexterm>.
+ La mémoire partagée est réservée en appelant :
+<programlisting>void RequestAddinShmemSpace(int size)
+</programlisting>
+ à partir de votre fonction <function>_PG_init</function>.
+ </para>
+ <para>
+ Les LWLocks sont réservés en appelant :
+<programlisting>
+void RequestAddinLWLocks(int n)
+</programlisting>
+ à partir de <function>_PG_init</function>.
+ </para>
+ <para>
+ Pour éviter des cas rares possibles, chaque moteur devrait utiliser la
+ fonction <function>AddinShmemInitLock</function> lors de la connexion et
+ de l'initialisation de la mémoire partagée, comme indiquée ci-dessous :
+<programlisting>
+ static mystruct *ptr = NULL;
+
+ if (!ptr)
+ {
+ bool found;
+
+ LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
+ ptr = ShmemInitStruct("my struct name", size, &found);
+ if (!ptr)
+ elog(ERROR, "out of shared memory");
+ if (!found)
+ {
+ initialize contents of shmem area;
+ acquire any requested LWLocks using:
+ ptr->mylockid = LWLockAssign();
+ }
+ LWLockRelease(AddinShmemInitLock);
+ }
+</programlisting>
+ </para>
+ </sect2>
+
+ </sect1>
Plus d'informations sur la liste de diffusion Trad