Chapitre 35. PL/pgSQL - Langage de procédures SQL

Table des matières
35.1. Aperçu
35.1.1. Avantages de l'utilisation de PL/pgSQL
35.1.2. Arguments supportés et types de données résultats
35.2. Astuces pour Développer en PL/pgSQL
35.3. Structure de PL/pgSQL
35.4. Déclarations
35.4.1. Alias de paramètres de fonctions
35.4.2. Copie de types
35.4.3. Types ligne
35.4.4. Types Record
35.4.5. RENAME
35.5. Expressions
35.6. Instructions de base
35.6.1. Assignation
35.6.2. SELECT INTO
35.6.3. Exécuter une expression ou requête sans résultat
35.6.4. Ne rien faire du tout
35.6.5. Exécuter des Commandes Dynamiques
35.6.6. Obtention du Statut du Résultat
35.7. Structures de contrôle
35.7.1. Retour d'une fonction
35.7.2. Contrôles conditionnels
35.7.3. Boucles simples
35.7.4. Boucler dans les résultats de requêtes
35.7.5. Récupérer les erreurs
35.8. Curseurs
35.8.1. Déclaration de variables curseur
35.8.2. Ouverture De Curseurs
35.8.3. Utilisation des Curseurs
35.9. Erreurs et Messages
35.10. Procédures déclencheur
35.11. Portage d'Oracle PL/SQL
35.11.1. Exemples de portages
35.11.2. Autres choses à surveiller
35.11.3. Annexe

PL/pgSQL est un langage de procédures chargeable pour le système de bases de données PostgreSQL. Les objectifs de la conception de PL/pgSQL ont été de créer un langage de procédures chargeable qui

Exception faites des conversions d'entrées/sorties et des fonctions de traitement pour les types définis par l'utilisateur, tout ce qui peut être défini dans les fonctions en langage C peut aussi être fait avec PL/pgSQL. Par exemple, il est possible de créer des fonctions de traitement conditionnel complexes et, par la suite, de les utiliser pour définir des opérateurs ou de les utiliser dans des expressions d'index.

35.1. Aperçu

Le gestionnaire d'appel PL/pgSQL découpe le texte source de la fonction et produit un arbre d'instructions binaires internes au premier appel de la fonction (au sein de chaque session). L'arbre d'instructions traduit complètement la structure de l'expression PL/pgSQL, mais les expressions SQL individuelles et les commandes SQL utilisées dans la fonction ne sont pas traduites immédiatement.

Chaque expression et commande SQL étant d'abord utilisée dans la fonction, l'interpréteur PL/pgSQL crée un plan d'exécution élaboré (en utilisant les fonctions SPI_prepare et SPI_saveplan du gestionnaire SPI). Les visites suivantes à cette expression ou commande réutilisent le plan élaboré. Ainsi, une fonction avec du code conditionnel qui contient de nombreuses expressions pour lesquelles des plans d'exécution pourraient être nécessaires ne feront que préparer et sauvegarder ces plans, qui ne sont réellement utilisés que durant le temps de vie de la connexion à la base de données. Ceci peut réduire substantiellement le temps total nécessaire à l'analyse syntaxique, et générer des plans d'exécution pour les expressions d'une fonction PL/pgSQL. Un inconvénient est que les erreurs d'une expression ou commande particulière peuvent ne pas être détectée jusqu'à ce que cette partie de la fonction soit atteinte au cours de l'exécution.

Une fois que PL/pgSQL a créé un plan d'exécution pour une commande de fonction particulière, il réutilisera ce plan pour le temps que durera la connexion à la base de données. C'est généralement un gain de performances, mais cela peut causer quelques problèmes si vous modifiez dynamiquement votre schéma de base de données. Par exemple

CREATE FUNCTION remplit() RETURNS integer AS $$
DECLARE
    -- declarations
BEGIN
    PERFORM ma_fonction();
END;
$$ LANGUAGE plpgsql;

Si vous exécutez la fonction ci-dessus, l'OID de ma_fonction() sera référencé dans le plan d'exécution produit pour l'expression PERFORM. Par la suite, si vous détruisez et recréez ma_fonction(), remplit() ne sera plus en mesure de trouver ma_fonction(). Vous auriez alors à recréer remplit(), ou au moins à lancer une nouvelle connexion à la base de donnée pour faire en sorte de la compiler à nouveau. Un autre moyen d'éviter ce problème est d'utiliser CREATE OR REPLACE FUNCTION lors de la mise à jour de la définition de ma_fonction (quand une fonction est << remplacée >>, son OID n'est pas changé).

Comme PL/pgSQL sauvegarde les plans d'exécution de cette façon, les commandes SQL qui apparaissent directement dans une fonction PL/pgSQL doivent se référer aux mêmes tables et colonnes pour chaque exception; en fait, vous ne pouvez pas utiliser un paramètre tel que le nom d'une table ou d'une colonne dans une commande SQL. Pour contourner cette restriction, vous pouvez construire des commandes dynamiques en utilisant l'expression PL/pgSQL EXECUTE — au prix de la construction d'un nouveau plan d'exécution pour chaque exécution.

Note : L'expression PL/pgSQL EXECUTE n'a pas de rapport avec l'instruction SQL EXECUTE supportée par le serveur PostgreSQL. L'expression EXECUTE du serveur ne peut pas être utilisée au sein des fonctions PL/pgSQL (et n'est pas nécessaire).

35.1.1. Avantages de l'utilisation de PL/pgSQL

SQL est le langage que PostgreSQL et la plupart des autres bases de données relationnelles utilisent comme langage de requête. Il est portable et facile à apprendre, mais chaque expression SQL doit être exécutée individuellement par le serveur de bases de données.

Cela signifie que votre application client doit envoyer chaque requête au serveur de bases de données, attendre que celui-ci la traite, recevoir les résultats, faire quelques traitements, et enfin envoyer d'autres requêtes au serveur. Tout ceci induit des communications interprocessus et peut aussi induire une surcharge du réseau si votre client est sur une machine différente du serveur de bases de données.

Grâce à PL/pgSQL vous pouvez grouper un bloc de traitement et une série de requêtes au sein du serveur de bases de données, et bénéficier ainsi de la puissance d'un langage de procédures, tout en gagnant du temps puisque vous évitez toute la charge de la communication client/serveur. Ceci peut permettre un gain de performances considérable.

Ainsi, avec PL/pgSQL vous pouvez utiliser tous les types de données, opérateurs et fonctions du SQL.

35.1.2. Arguments supportés et types de données résultats

Les fonctions écrites en PL/pgSQL peuvent accepter comme argument n'importe quel type de données supporté par le serveur, et peuvent renvoyer un résultat de n'importe lequel de ces types. Elles peuvent aussi accepter ou renvoyer n'importe quel type composite (type ligne) spécifié par nom. Il est aussi possible de déclarer une fonction PL/pgSQL renvoyant un type record, signifiant que le résultat est un type ligne dont les colonnes sont déterminées par spécification dans la requête appelante (voir la Section 7.2.1.4).

Les fonctions PL/pgSQL peuvent aussi être déclarées comme acceptant et renvoyant les types << polymorphes >>, anyelement et anyarray. Le type de données réel géré par une fonction polymorphe peut varier d'appel en appel (voir la Section 31.2.1). Voir l'exemple de la Section 35.4.1.

Les fonctions PL/pgSQL peuvent aussi être déclarées comme devant renvoyer un << ensemble >> ou une table de n'importe lequel des type de données dont elles peuvent renvoyer une instance unique. De telles fonctions génèrent leur sortie en exécutant RETURN NEXT pour chaque élément désiré de l'ensemble résultat.

Enfin, une fonction PL/pgSQL peut être déclarée comme renvoyant void si elle n'a pas de valeur de retour utile.

PL/pgSQL n'a pas actuellement de support complet pour les types de domaine : il traite un domaine de la même façon qu'un type scalaire sous-jacent. Ceci signifie que les contraintes associées avec le domaine ne seront pas forcées. Ceci n'est pas un problème pour les arguments des fonctions mais déclarer une fonction PL/pgSQL renvoyant un type domaine est hasardeux.