Attention, tous les exemples de cet article utilisent le langage PL/pgsql. Pour l'utiliser, vous devez l'intégrer à votre base. Cela se fait tout simplement avec cette commande :

createlang plpgsql metier

Je viens donc d'intégrer le langage de procédures plpgsql dans la base metier.

Avant la version 8.1, PostgreSQL n'acceptait que des arguments en entrée dans la déclaration des fonctions (et quelque soit le langage de procédures utilisé). Pour renvoyer un résultat, il fallait passer par la « valeur de retour ». Malgré tout, il était possible de renvoyer plusieurs valeurs en utilisant un type composite. En fait, cela revenait à renvoyer une ligne de plusieurs colonnes d'une table « virtuelle ».

Créons une première fonction servant uniquement à incrémenter la valeur en entrée :

CREATE FUNCTION incremente(valeur int4) RETURNS int4 AS
$$
BEGIN
RETURN valeur + 1;
END
$$ LANGUAGE plpgsql;

« RETURNS int4 » (sur la première ligne) indique le type de la valeur renvoyée (ici un entier, plus précisément un int4). « RETURN ... » (4è ligne) sert à indiquer la valeur à renvoyer. Voici les résultats sur deux tests :

metier=# SELECT incremente(1);
incremente
------------
2
(1 ligne)

metier=# SELECT incremente(2);
incremente
------------
3
(1 ligne)

Créons maintenant une fonction renvoyant plusieurs valeurs. Nous devons passer par un type composite, équivalent d'une structure en C :

CREATE TYPE inc AS (
val1 int4,
val2 int4,
str varchar(10));

Une fois ce type déclaré, nous pouvons passer à la création de la procédure stockée :

CREATE FUNCTION incremente_2(valeur int4) RETURNS inc AS
$$
DECLARE
r inc;
BEGIN
SELECT INTO r valeur + 1, valeur + 2, 'test';
RETURN r;
END
$$ LANGUAGE plpgsql;

Comme précédemment, « RETURNS ... » indique le type (composite dans ce cas) renvoyé et « RETURN ... » indique la valeur renvoyée. Voici quelques exemples d'utilisation de la fonction :

metier=# SELECT incremente_2(5);
incremente
------------
(6,7,test)
(1 ligne)

metier=# SELECT (incremente_2(5)).val1;
val1
------
6
(1 ligne)

metier=# SELECT (incremente_2(5)).str;
str
------
test
(1 ligne)

La fonction utilise tous les concepts des types composites, ce qui en rend la gestion un peu lourde. Elle ne renvoie pas réellement une ligne d'un tableau mais un type ROW. De plus, elle oblige à créer un type séparément.

La version 8.1 apporte une solution élégante à ce problème grâce à deux nouveaux types de paramètres : un paramètre en sortie seule (OUT) et un paramètre en entrée/sortie (INOUT). Sans indication, un paramètre est en entrée. Pour indiquer le type de paramètre, il faut l'écrire avant le nom de la variable et/ou son type de variable).

Commençons par le type OUT.

CREATE FUNCTION incremente81(IN int4, OUT int4) AS
$$
BEGIN
$2 = $1 + 1;
END;
LANGUAGE plpgsql;

Premier point intéressant, je n'ai pas indiqué de type en retour (avec l'élement RETURNS). Deuxième point intéressant qui en découle, il n'y a pas non plus d'instruction RETURN. La valeur renvoyée sera le seul paramètre déclaré en sortie, $2 dans cet exemple. Exécutons cette fonction :

metier=# SELECT incremente81(5);
incremente81
----------------
6
(1 ligne)

Elle fonctionne exactement comme notre ancienne fonction « incremente ». Remarquez simplement que je n'ai indiqué qu'un seul paramètre. Le paramètre de type OUT ne peut pas être fourni. Vous ne pouvez pas non plus y placer une variable comme dans le cas des fonctions avec paramètres par référence dans des langages comme le C. Tous les paramètres OUT et INOUT sont renvoyés comme valeur de retour. Continuons avec le type INOUT qui vous permet d'y stocker une valeur et de la récupérer modifiée en sortie de la fonction.

CREATE FUNCTION incremente81(INOUT int4) AS
$$
BEGIN
$1 = $1 + 1;
END;
$$ LANGUAGE plpgsql;

Là aussi, pas de RETURNS et de RETURN.

metier=# SELECT incremente81(5);
incremente81
----------------
6
(1 ligne)

Ce type est surtout utile pour éviter de déclarer une variable en entrée et une variable en sortie quand elles sont du même type.

Pour en revenir à notre exemple complexe où plusieurs valeurs sont renvoyées par une seule fonction, voici comment elle s'écrirait avec cette nouvelle fonctionnalité :

CREATE FUNCTION incremente81_2(IN valeur int4, OUT val1 int4, OUT val2 int4, OUT str varchar(10)) AS
$$
BEGIN
val1 = valeur + 1;
val2 = valeur + 2;
str = 'test';
END
$$ LANGUAGE plpgsql;

Pas de déclaration d'un type composite, pas de RETURNS et de RETURN : la valeur renvoyée et son type dépendent du nombre d'arguments déclarés en sortie (donc OUT et INOUT). Remarquez aussi que je n'indique rien pour les paramètres OUT. S'il y a plusieurs arguments, l'élément renvoyé sera de type composite. Si les arguments sont nommés comme dans l'exemple ci-dessus, il sera possible d'accéder à chaque élément :

metier=# SELECT incremente81_2(5);
incremente
------------
(6,7,test)
(1 ligne)

metier=# SELECT (incremente81_2(5)).val1;
val1
------
6
(1 ligne)

metier=# SELECT (incremente81_2(5)).str;
str
------
test
(1 ligne)

L'écriture de la fonction est plus intuitive, sa compréhension est plus simple (par exemple, il n'est plus nécessaire de savoir ce que représente le type inc).

Cette fonctionnalité a aussi été ajoutée pour faciliter le passage d'Oracle vers PostgreSQL. Oracle propose aussi ce type d'arguments. Il propose en plus le passage de variables par référence, ce que PostgreSQL ne fait pas (encore ?).