

Les structures de contrôle sont probablement la partie la plus utile (et importante) de PL/pgSQL. Grâce aux structures de contrôle de PL/pgSQL, vous pouvez manipuler les données PostgreSQL de façon très flexible et puissante.
     Il y a deux commandes disponibles qui vous permettent de renvoyer des données
     d'une fonction : RETURN et RETURN
     NEXT.
    
RETURNRETURN expression;
    
     RETURN accompagné d'une expression termine la fonction et
     renvoie le valeur de l'expression à l'appelant.
     Cette forme doit être utilisée avec des fonctions PL/pgSQL
     qui ne renvoient pas d'ensemble de valeurs.
    
Dans une fonction qui renvoie un type scalaire, le résultat de l'expression sera automatiquement convertie dans le type que la fonction renvoie. Mais pour renvoyer une valeur composite (ligne), vous devez écrire une expression renvoyant exactement l'ensemble de colonnes souhaité. Ceci peut demander l'utilisation de conversion explicite.
     Si vous déclarez la fonction avec des paramètres en sortie, écrivez
     seulement RETURN sans expression. Les valeurs
     courantes des paramètres en sortie seront renvoyées.
    
     Si vous déclarez que la fonction renvoie void, une
     instruction RETURN peut être utilisée pour quitter
     rapidement la fonction ; mais n'écrivez pas d'expression après
     RETURN.
    
     La valeur de retour d'une fonction ne peut pas être laissée indéfinie.
     Si le contrôle atteint la fin du bloc de haut niveau de la fonction,
     sans parvenir à une instruction RETURN, une erreur
     d'exécution survient. Néanmoins, cette restriction ne s'applique pas
     aux fonctions sans paramètre de sortie et aux fonctions renvoyant
     void. Dans ces cas, une instruction
     RETURN est automatiquement exécutée si le bloc de
     haut niveau est terminé.
    
Quelques exemples :
-- fonctions renvoyant un type scalaire
RETURN 1 + 2;
RETURN scalar_var;
-- fonctions renvoyant un type composite
RETURN composite_type_var;
RETURN (1, 2, 'three'::text);  -- must cast columns to correct types
     
RETURN NEXT et RETURN QUERYRETURN NEXTexpression; RETURN QUERYrequete; RETURN QUERY EXECUTEcommand-string[ USINGexpression[, ...] ];
     Quand une fonction PL/pgSQL déclare renvoyer
     SETOF , la
     procédure à suivre est un peu différente. Dans ce cas, les éléments
     individuels à renvoyer sont spécifiés par une séquence de commandes
     un_certain_typeRETURN NEXT ou RETURN QUERY, suivies
     de la commande finale RETURN sans argument qui est
     utilisée pour indiquer la fin de l'exécution de la fonction.
     RETURN NEXT peut être utilisé avec des types de données
     scalaires comme composites ; avec un type de résultat composite, une
     « table » entière de résultats sera renvoyée.
     RETURN QUERY ajoute les résultats de l'exécution d'une
     requête à l'ensemble des résultats de la fonction. RETURN
      NEXT et RETURN QUERY peuvent être
     utilisés dans la même fonction, auquel cas leurs résultats seront
     concaténées.
    
     RETURN NEXT et RETURN
      QUERY ne quittent pas réellement la fonction  --  elles
     ajoutent simplement zéro ou plusieurs lignes à l'ensemble de résultats
     de la fonction. L'exécution continue ensuite avec l'instruction
     suivante de la fonction PL/pgSQL. Quand
     plusieurs commandes RETURN NEXT et/ou RETURN
      QUERY successives sont exécutées, l'ensemble de résultats
     augmente. Un RETURN, sans argument, permet
     de quitter la fonction mais vous pouvez aussi continuer jusqu'à la fin
     de la fonction.
    
     RETURN QUERY dispose d'une variante
     RETURN QUERY EXECUTE, qui spécifie la requête à exécuter
     dynamiquement. Les expressions de paramètres peuvent être insérées dans
     la chaîne calculée via USING, de la même façon que le
     fait la commande EXECUTE.
    
     Si vous déclarez la fonction avec des paramètres en sortie, écrivez
     RETURN NEXT sans expression. À chaque exécution,
     les valeurs actuelles des variables paramètres en sortie seront
     sauvegardées pour un renvoi éventuel en tant que résultat en sortie.
     Notez que vous devez déclarer la fonction en tant que
     SETOF record quand il y a plusieurs paramètres en
     sortie, ou SETOF 
     quand il y a un seul paramètre en sortie, et de type
     un_certain_typeun_certain_type, pour créer une fonction SRF
     avec des paramètres en sortie.
    
     Voici un exemple d'une fonction utilisant RETURN
      NEXT :
     
CREATE TABLE truc (id_truc INT, sousid_truc INT, nom_truc TEXT);
INSERT INTO truc VALUES (1, 2, 'trois');
INSERT INTO truc VALUES (4, 5, 'six');
CREATE OR REPLACE FUNCTION obtenir_tous_les_trucs() RETURNS SETOF foo AS
$BODY$
DECLARE
    r truc%rowtype;
BEGIN
    FOR r IN
        SELECT * FROM truc WHERE id_truc > 0
    LOOP
        -- quelques traitements
        RETURN NEXT r; -- renvoie la ligne courante du SELECT
    END LOOP;
    RETURN;
END;
$BODY$
LANGUAGE plpgsql;
SELECT * FROM obtenir_tous_les_trucs();
     
     Voici un exemple de fonction utilisant RETURN
      QUERY :
     
CREATE FUNCTION obtient_idvol_disponibles(date) RETURNS SETOF integer AS
$BODY$
BEGIN
    RETURN QUERY SELECT idvol
                   FROM vol
                  WHERE datevol >= $1
                    AND datevol < ($1 + 1);
    -- Comme l'exécution n'est pas terminée, nous vérifions si les lignes
    -- ont été renvoyées et levons une exception dans le cas contraire.
    IF NOT FOUND THEN
        RAISE EXCEPTION 'Aucun vol à %.', $1;
    END IF;
    RETURN;
 END
$BODY$
LANGUAGE plpgsql;
-- Renvoie les vols disponibles ou lève une exception si aucun vol
-- n'est disponible.
SELECT * FROM obtient_idvol_disponibles(CURRENT_DATE);
     
      L'implémentation actuelle de RETURN NEXT et de
      RETURN QUERY pour
      PL/pgSQL récupère la totalité de l'ensemble des
      résultats avant
      d'effectuer le retour de la fonction, comme vu plus haut. Cela signifie que
      si une fonction PL/pgSQL produit une structure résultat
      très grande, les performances peuvent être faibles : les données seront
      écrites sur le disque pour éviter un épuisement de la mémoire mais la fonction
      en elle-même ne renverra rien jusqu'à ce que l'ensemble complet des résultats
      soit généré. Une version future de  PL/pgSQL
      permettra aux utilisateurs de définir des fonctions renvoyant des ensembles qui
      n'auront pas cette limitation. Actuellement, le point auquel les données commencent
      à être écrites sur le disque est contrôlé par la variable de configuration
      work_mem. Les administrateurs
      ayant une mémoire suffisante pour enregistrer des ensembles de résultats
      plus importants en mémoire doivent envisager l'augmentation de ce
      paramètre.
     
    Une procédure n'a pas de valeur de retour. De ce fait, une procédure peut
    se terminer sans instruction RETURN. Si vous souhaitez
    utiliser l'instruction RETURN pour quitter le code en
    avance, écrivez juste RETURN sans expression.
   
Si une procédure a des paramètres en sortie, les valeurs finales des paramètres en sortie seront renvoyées à l'appelant.
    Une fonction, une procédure et un bloc DO en
    PL/pgSQL peut appeler une procédure en
    appelant CALL. Les paramètres en sortie sont gérées
    différemment de la façon dont CALL fonctionne en SQL.
    Chaque paramètre INOUT de la procédure doit
    correspondre à une variable dans l'instruction CALL et
    le retour de la procédure est affecté à cette variable au retour. Par
    exemple :
    
CREATE PROCEDURE triple(INOUT x int)
LANGUAGE plpgsql
AS $$
BEGIN
    x := x * 3;
END;
$$;
DO $$
DECLARE myvar int := 5;
BEGIN
  CALL triple(myvar);
  RAISE NOTICE 'myvar = %', myvar;  -- prints 15
END
$$;
    
    Les instructions IF et CASE vous permettent d'exécuter des commandes
    basées sur certaines conditions. PL/pgSQL a trois formes de
    IF :
    
IF ... THEN ... END IF
IF ... THEN ... ELSE ... END IF
IF ... THEN ... ELSIF ... THEN ... ELSE ... END IF
    et deux formes de CASE :
    
CASE ... WHEN ... THEN ... ELSE ... END CASE
CASE WHEN ... THEN ... ELSE ... END CASE
IF-THENIFexpression-booleenneTHENinstructionsEND IF;
     Les instructions IF-THEN sont la forme la plus simple de
     IF. Les instructions entre THEN et
     END IF seront exécutées si la condition est vraie. Autrement,
     elles seront ignorées.
    
Exemple :
IF v_id_utilisateur <> 0 THEN
    UPDATE utilisateurs SET email = v_email WHERE id_utilisateur = v_id_utilisateur;
END IF;
     
IF-THEN-ELSEIFexpression-booleenneTHENinstructionsELSEinstructionsEND IF;
     Les instructions IF-THEN-ELSE s'ajoutent au
     IF-THEN en vous permettant de spécifier un autre ensemble
     d'instructions à exécuter si la condition n'est pas vraie (notez que ceci
     inclut le cas où la condition s'évalue à NULL.).
    
Exemples :
IF id_parent IS NULL OR id_parent = ''
THEN
    RETURN nom_complet;
ELSE
    RETURN hp_true_filename(id_parent) || '/' || nom_complet;
END IF;
     
IF v_nombre > 0 THEN
    INSERT INTO nombre_utilisateurs (nombre) VALUES (v_nombre);
    RETURN 't';
ELSE
    RETURN 'f';
END IF;
     
IF-THEN-ELSIFIFexpression-booleenneTHENinstructions[ ELSIFexpression-booleenneTHENinstructions[ ELSIFexpression-booleenneTHENinstructions... ] ] [ ELSEinstructions] END IF;
     Quelques fois, il existe plus de deux alternatives.
     IF-THEN-ELSIF fournit une méthode agréable pour
     vérifier différentes alternatives. Les conditions
     IF sont testées successivement jusqu'à trouver la
     bonne. Alors les instructions associées sont exécutées, puis le
     contrôle est passé à la prochaine instruction après END
      IF. (Toute autre condition IF n'est
     pas testée.) Si aucune des conditions
     IF n'est vraie, alors le bloc
     ELSE (s'il y en a un) est exécuté.
    
Voici un exemple :
IF nombre = 0 THEN
    resultat := 'zero';
ELSIF nombre > 0 THEN
    resultat := 'positif';
ELSIF nombre < 0 THEN
    resultat := 'negatif';
ELSE
    -- hmm, la seule possibilité est que le nombre soit NULL
    resultat := 'NULL';
END IF;
     
     Le mot clé ELSIF peut aussi s'écrire
     ELSEIF.
    
     Une façon alternative d'accomplir la même tâche est d'intégrer les
     instructions IF-THEN-ELSE, comme dans l'exemple
     suivant :
     
IF demo_row.sex = 'm' THEN
    pretty_sex := 'man';
ELSE
    IF demo_row.sex = 'f' THEN
        pretty_sex := 'woman';
    END IF;
END IF;
     
     Néanmoins, cette méthode requiert d'écrire un END
      IF pour chaque IF, donc c'est un peu plus
     compliqué que d'utiliser ELSIF quand il y a beaucoup
     d'autres alternatives.
    
CASE simpleCASEexpression_rechercheWHENexpression[,expression[ ... ]] THENinstructions[ WHENexpression[,expression[ ... ]] THENinstructions... ] [ ELSEinstructions] END CASE;
     La forme simple de CASE fournit une exécution
     conditionnelle basée sur l'égalité des opérandes.
     L'expression-recherche est évaluée (une fois)
     puis comparée successivement à chaque expression
     dans les clauses WHEN. Si une correspondance est
     trouvée, alors les instructions correspondantes
     sont exécutées, puis le contrôle est passé à la prochaine instruction
     après END CASE. (Les autres expressions
     WHEN ne sont pas testées.) Si aucune correspondance
     n'est trouvée, les instructions du bloc
     ELSE sont exécutées ; s'il n'y a pas de bloc
     ELSE, une exception CASE_NOT_FOUND
     est levée.
    
Voici un exemple simple :
CASE x
    WHEN 1, 2 THEN
        msg := 'un ou deux';
    ELSE
        msg := 'autre valeur que un ou deux';
END CASE;
     
CASE recherché
CASE
    WHEN expression_booléenne THEN
      instructions
  [ WHEN expression_booléenne THEN
      instructions
    ... ]
  [ ELSE
      instructions ]
END CASE;
    
     La forme recherchée de CASE fournit une exécution
     conditionnelle basée sur la vérification d'expressions booléennes.
     Chaque expression-booléenne de la clause
     WHEN est évaluée à son tour jusqu'à en trouver
     une qui est validée (true). Les
     instructions correspondantes sont exécutées,
     puis le contrôle est passé à la prochaine instruction après END
      CASE. (Les expressions WHEN suivantes ne
     sont pas testées.) Si aucun résultat vrai n'est trouvé, les
     instructions du bloc ELSE
     sont exécutées. Si aucun bloc ELSE n'est présent, une
     exception CASE_NOT_FOUND est levée.
    
Voici un exemple :
CASE
    WHEN x BETWEEN 0 AND 10 THEN
        msg := 'valeur entre zéro et dix';
    WHEN x BETWEEN 11 AND 20 THEN
        msg := 'valeur entre onze et vingt';
END CASE;
     
     Cette forme de CASE est entièrement équivalente à
     IF-THEN-ELSIF, sauf pour la règle qui dit qu'atteindre
     une clause ELSE omise résulte dans une erreur plutôt
     que ne rien faire.
    
    Grâce aux instructions LOOP, EXIT,
    CONTINUE, WHILE
    FOR et FOREACH, vous pouvez faire en sorte que vos fonctions
    PL/pgSQL répètent une série de commandes.
   
LOOP[<<label>>] LOOPinstructionsEND LOOP [label];
     LOOP définit une boucle inconditionnelle répétée indéfiniment
     jusqu'à ce qu'elle soit terminée par une instruction EXIT ou
     RETURN.  Le label optionnel
     peut être utilisé par les instructions EXIT et
     CONTINUE dans le cas de boucles imbriquées pour définir la
     boucle impliquée.
    
EXITEXIT [label] [ WHENexpression-booléenne];
     Si aucun label n'est donné, la boucle la plus
     imbriquée se termine et l'instruction suivant END LOOP est
     exécutée.
     Si un label est donné, ce doit être
     le label de la boucle, du bloc courant ou d'un niveau moins imbriqué.
     La boucle ou le bloc nommé se termine alors et le contrôle continue
     avec l'instruction située après le END de la boucle ou du bloc
     correspondant.
    
     Si WHEN est spécifié, la sortie de boucle ne s'effectue que
     si expression-booléenne est vraie. Sinon, le contrôle passe à
     l'instruction suivant le EXIT.
    
     EXIT peut être utilisé pour tous les types de
     boucles ; il n'est pas limité aux boucles non conditionnelles.
    
     Lorsqu'il est utilisé avec un bloc BEGIN,
     EXIT passe le contrôle à la prochaine instruction
     après la fin du bloc. Notez qu'un label doit être utilisé pour
     cela ; un EXIT sans label n'est jamais pris
     en compte pour correspondre à un bloc BEGIN.
     (Ceci est un changement de la version 8.4 de
     PostgreSQL. Auparavant, il était permis
     de faire correspondre un EXIT sans label avec un
     bloc BEGIN.)
    
Exemples :
LOOP
    -- quelques traitements
    IF nombre > 0 THEN
        EXIT;  -- sortie de boucle
    END IF;
END LOOP;
LOOP
    -- quelques traitements
    EXIT WHEN nombre > 0;
END LOOP;
<<un_bloc>>
BEGIN
    -- quelques traitements
    IF stocks > 100000 THEN
        EXIT un_bloc;  -- cause la sortie (EXIT) du bloc BEGIN
    END IF;
    -- les traitements ici seront ignorés quand stocks > 100000
END;
     
CONTINUECONTINUE [label] [ WHENexpression-booléenne];
     Si aucun label n'est donné, la prochaine
     itération de la boucle interne est commencée. C'est-à-dire que toutes
     les instructions restantes dans le corps de la boucle sont ignorées et
     le contrôle revient à l'expression de contrôle de la boucle pour
     déterminer si une autre itération de boucle est nécessaire.
     Si le label est présent, il spécifie le label
     de la boucle dont l'exécution va être continuée.
    
     Si WHEN est spécifié, la prochaine itération de la boucle
     est commencée seulement si l'expression-booléenne est vraie.
     Sinon, le contrôle est passé à l'instruction après
     CONTINUE.
    
     CONTINUE peut être utilisé avec tous les types de
     boucles ; il n'est pas limité à l'utilisation des boucles
     inconditionnelles.
    
Exemples :
LOOP
    -- quelques traitements
    EXIT WHEN nombre > 100;
    CONTINUE WHEN nombre < 50;
    -- quelques traitements pour nombre IN [50 .. 100]
END LOOP;
     
WHILE[<<label>>] WHILEexpression-booléenneLOOPinstructionsEND LOOP [label];
     L'instruction WHILE répète une séquence d'instructions aussi longtemps
     que expression-booléenne est évaluée à vrai. L'expression est vérifiée juste
     avant chaque entrée dans le corps de la boucle.
    
Par exemple :
WHILE montant_possede > 0 AND balance_cadeau > 0 LOOP
    -- quelques traitements ici
END LOOP;
WHILE NOT termine LOOP
    -- quelques traitements ici
END LOOP;
     
FOR (variante avec entier)[<<label>>] FORnomIN [ REVERSE ]expression..expression[ BYexpression] LOOPinstructionEND LOOP [label];
     Cette forme de FOR crée une boucle qui effectue une itération
     sur une plage de valeurs entières. La variable nom
     est automatiquement définie comme un type integer et n'existe
     que dans la boucle (toute définition de la variable est ignorée à l'intérieur
     de la boucle). Les deux expressions donnant les limites inférieures et
     supérieures de la plage sont évaluées une fois en entrant dans la boucle.
     Si la clause BY n'est pas spécifiée, l'étape
     d'itération est de 1, sinon elle est de la valeur spécifiée dans la
     clause BY, qui est évaluée encore une fois à l'entrée
     de la boucle. Si REVERSE est indiquée,
     alors la valeur de l'étape est soustraite, plutôt qu'ajoutée, après chaque
     itération.
    
     Quelques exemples de boucles FOR avec entiers :
     
FOR i IN 1..10 LOOP
    -- prend les valeurs 1,2,3,4,5,6,7,8,9,10 dans la boucle
END LOOP;
FOR i IN REVERSE 10..1 LOOP
    -- prend les valeurs 10,9,8,7,6,5,4,3,2,1 dans la boucle
END LOOP;
FOR i IN REVERSE 10..1 BY 2 LOOP
    -- prend les valeurs 10,8,6,4,2 dans la boucle
END LOOP;
     
     Si la limite basse est plus grande que la limite haute (ou moins grande
     dans le cas du REVERSE), le corps de la boucle
     n'est pas exécuté du tout. Aucune erreur n'est renvoyée.
    
     Si un label est attaché à la boucle
     FOR, alors la variable entière de boucle peut être
     référencée avec un nom qualifié en utilisant ce
     label.
    
    En utilisant un type de FOR différent, vous pouvez itérer au travers
    des résultats d'une requête et par là-même manipuler ces données. La
    syntaxe est la suivante :
    
[<<label>>] FORcibleINrequêteLOOPinstructionsEND LOOP [label];
    La cible est une variable de type record, row
    ou une liste de variables scalaires séparées par une virgule. La
    cible est affectée successivement à chaque ligne
    résultant de la requête et le corps de la boucle
    est exécuté pour chaque ligne. Voici un exemple :
    
CREATE FUNCTION rafraichir_vuemat() RETURNS integer AS $$
DECLARE
    vues_mat RECORD;
BEGIN
    RAISE NOTICE 'Rafraichissement de toutes les vues matérialisées...';
    FOR mviews IN
       SELECT n.nspname AS mv_schema,
              c.relname AS mv_name,
              pg_catalog.pg_get_userbyid(c.relowner) AS owner
         FROM pg_catalog.pg_class c
    LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
        WHERE c.relkind = 'm'
     ORDER BY 1
    LOOP
        -- Maintenant "mviews" contient un enregistrement avec les informations sur la vue matérialisée
        RAISE NOTICE 'Rafraichissement de la vue matérialisée %.% (propriétaire : %)...',
                     quote_ident(mviews.mv_schema),
                     quote_ident(mviews.mv_name),
                     quote_ident(mviews.owner);
        EXECUTE format('REFRESH MATERIALIZED VIEW %I.%I', mviews.mv_schema, mviews.mv_name);
    END LOOP;
    RAISE NOTICE 'Fin du rafraichissement des vues matérialisées.';
    RETURN 1;
END;
$$ LANGUAGE plpgsql;
    
    Si la boucle est terminée par une instruction EXIT, la dernière valeur
    ligne affectée est toujours accessible après la boucle.
   
    La requête utilisée dans ce type d'instruction
    FOR peut être toute commande SQL qui renvoie des lignes
    à l'appelant : SELECT est le cas le plus commun
    mais vous pouvez aussi utiliser INSERT, UPDATE
    ou DELETE avec une clause RETURNING.
    Certaines commandes comme EXPLAIN fonctionnent aussi.
   
Les variables PL/pgSQL sont substituées dans le texte de la requête et le plan de requête est mis en cache pour une réutilisation possible. C'est couvert en détail dans la Section 42.11.1 et dans la Section 42.11.2.
    L'instruction FOR-IN-EXECUTE est un moyen d'itérer sur des
    lignes :
    
[<<label>>] FORtargetIN EXECUTEtext_expression[ USINGexpression[, ...] ] LOOPinstructionsEND LOOP [label];
    Ceci est identique à la forme précédente, à ceci près que l'expression
    de la requête source est spécifiée comme une expression chaîne,
    évaluée et replanifiée à chaque entrée dans la boucle FOR. Ceci
    permet au développeur de choisir entre la vitesse d'une requête préplanifiée et la
    flexibilité d'une requête dynamique, uniquement avec l'instruction
    EXECUTE.
    Comme avec EXECUTE, les valeurs de paramètres peuvent
    être insérées dans la commande dynamique via USING.
   
Une autre façon de spécifier la requête dont les résultats devront être itérés est de la déclarer comme un curseur. Ceci est décrit dans Section 42.7.4.
    La boucle FOREACH ressemble beaucoup à une
    boucle FOR mais, au lieu d'itérer sur les lignes
    renvoyées par une requêtes SQL, elle itère sur les éléments d'une
    valeur de type tableau. (En général, FOREACH
    est fait pour boucler sur les composants d'une expression
    composite ; les variantes pour boucler sur des composites en
    plus des tableaux pourraient être ajoutées dans le futur.)
    L'instruction FOREACH pour boucler sur un
    tableau est :
    
[ <<label>> ] FOREACHtarget[ SLICEnombre] IN ARRAYexpressionLOOPinstructionsEND LOOP [label];
    Sans SLICE ou si SLICE 0 est
    indiqué, la boucle itère au niveau des éléments individuels du
    tableau produit par l'évaluation de
    l'expression. La variable
    cible se voit affectée chaque valeur
    d'élément en séquence, et le corps de la boucle est exécuté pour
    chaque élément. Voici un exemple de boucle sur les éléments d'un
    tableau d'entiers :
    
CREATE FUNCTION somme(int[]) RETURNS int8 AS $$
DECLARE
  s int8 := 0;
  x int;
BEGIN
  FOREACH x IN ARRAY $1
  LOOP
    s := s + x;
  END LOOP;
  RETURN s;
END;
$$ LANGUAGE plpgsql;
    
    Les éléments sont parcourus dans l'ordre de leur stockage, quelque
    soit le nombre de dimensions du tableau. Bien que la
    cible est habituellement une simple
    variable, elle peut être une liste de variables lors d'une boucle
    dans un tableau de valeurs composites (des enregistrements). Dans
    ce cas, pour chaque élément du tableau, les variables se voient
    affectées les colonnes de la valeur composite.
   
    Avec une valeur SLICE positive,
    FOREACH itère au travers des morceaux du tableau
    plutôt que des éléments seuls. La valeur de SLICE
    doit être un entier constant, moins large que le nombre de
    dimensions du tableau. La variable cible
    doit être un tableau et elle reçoit les morceaux successifs de la
    valeur du tableau, où chaque morceau est le nombre de dimensions
    indiquées par SLICE. Voici un exemple d'itération
    sur des morceaux à une dimension :
    
CREATE FUNCTION parcourt_lignes(int[]) RETURNS void AS $$
DECLARE
  x int[];
BEGIN
  FOREACH x SLICE 1 IN ARRAY $1
  LOOP
    RAISE NOTICE 'ligne = %', x;
  END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT parcourt_lignes(ARRAY[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]);
NOTICE:  ligne = {1,2,3}
NOTICE:  ligne = {4,5,6}
NOTICE:  ligne = {7,8,9}
NOTICE:  ligne = {10,11,12}
    
    Par défaut, toute erreur survenant dans une fonction
    PL/pgSQL annule l'exécution de la fonction mais
    aussi de la transaction qui l'entoure. Vous pouvez récupérer les erreurs
    en utilisant un bloc BEGIN avec une clause
    EXCEPTION. La syntaxe est une extension de la syntaxe
    habituelle pour un bloc BEGIN :
    
[ <<label>> ] [ DECLAREdeclarations] BEGINinstructionsEXCEPTION WHENcondition[ ORcondition... ] THENinstructions_gestion_erreurs[ WHENcondition[ ORcondition... ] THENinstructions_gestion_erreurs... ] END;
    Si aucune erreur ne survient, cette forme de bloc exécute simplement
    toutes les instructions puis passe le
    contrôle à l'instruction suivant END. Mais si une erreur
    survient à l'intérieur des instructions,
    le traitement en cours des instructions est
    abandonné et le contrôle est passé à la liste d'EXCEPTION.
    Une recherche est effectuée sur la liste pour la première
    condition correspondant à l'erreur survenue.
    Si une correspondance est trouvée, les
    instructions_gestion_erreurs correspondantes
    sont exécutées puis le contrôle est passé à l'instruction suivant le
    END. Si aucune correspondance n'est trouvée, l'erreur se
    propage comme si la clause EXCEPTION n'existait pas du
    tout : l'erreur peut être récupérée par un bloc l'enfermant avec
    EXCEPTION ou, s'il n'existe pas, elle annule le traitement de
    la fonction.
   
    Les noms des condition sont indiquées dans
    l'Annexe A. Un nom de catégorie correspond à toute
    erreur contenue dans cette catégorie. Le nom de condition spéciale
    OTHERS correspond à tout type d'erreur sauf
    QUERY_CANCELED et ASSERT_FAILURE (il
    est possible, mais pas recommandé, de
    récupérer ces deux types d'erreur par leur nom). Les noms des
    conditions ne sont pas sensibles à la casse. De plus, une condition d'erreur
    peut être indiquée par un code SQLSTATE ; par exemple,
    ces deux cas sont équivalents :
    
        WHEN division_by_zero THEN ...
        WHEN SQLSTATE '22012' THEN ...
    
    Si une nouvelle erreur survient à l'intérieur des
    instructions_gestion_erreurs sélectionnées, elle
    ne peut pas être récupérée par cette clause EXCEPTION mais
    est propagée en dehors. Une clause EXCEPTION l'englobant
    pourrait la récupérer.
   
    Quand une erreur est récupérée par une clause EXCEPTION, les
    variables locales de la fonction PL/pgSQL restent
    dans le même état qu'au moment où l'erreur est survenue mais toutes les
    modifications à l'état persistant de la base de données à l'intérieur
    du bloc sont annulées. Comme exemple, considérez ce fragment :
    
INSERT INTO mon_tableau(prenom, nom) VALUES('Tom', 'Jones');
BEGIN
  UPDATE mon_tableau SET prenom = 'Joe' WHERE nom = 'Jones';
  x := x + 1;
  y := x / 0;
  EXCEPTION
    WHEN division_by_zero THEN
      RAISE NOTICE 'récupération de l''erreur division_by_zero';
RETURN x;
END;
    
    Quand le contrôle parvient à l'affectation de y, il échouera
    avec une erreur division_by_zero. Elle sera récupérée par la
    clause EXCEPTION. La valeur renvoyée par l'instruction
    RETURN sera la valeur incrémentée de x mais
    les effets de la commande UPDATE auront été annulés. La
    commande INSERT précédant le bloc ne sera pas annulée, du
    coup le résultat final est que la base de données contient
    Tom Jones et non pas Joe Jones.
   
     Un bloc contenant une clause EXCEPTION est
     significativement plus coûteuse en entrée et en sortie qu'un bloc
     sans. Du coup, n'utilisez pas EXCEPTION sans besoin.
    
Exemple 42.2. Exceptions avec UPDATE/INSERT
     Cet exemple utilise un gestionnaire d'exceptions pour réaliser soit un
     UPDATE soit un INSERT, comme approprié.
     Il est recommandé d'utiliser la commande INSERT
     avec la clause ON CONFLICT DO UPDATE plutôt
     que cette logique. Cet exemple ne sert qu'à illustrer l'usage des
     structures de contrôle de PL/pgSQL :
     
CREATE TABLE base (a INT PRIMARY KEY, b TEXT);
CREATE FUNCTION fusionne_base(cle INT, donnee TEXT) RETURNS VOID AS
$$
BEGIN
    LOOP
        -- commençons par tenter la mise à jour de la clé
        UPDATE base SET b = donnee WHERE a = cle;
        IF found THEN
            RETURN;
        END IF;
        -- si elle n'est pas dispo, tentons l'insertion de la clé
        -- si quelqu'un essaie d'insérer la même clé en même temps,
        -- il y aura une erreur pour violation de clé unique
        BEGIN
            INSERT INTO base(a,b) VALUES (cle, donnee);
            RETURN;
        EXCEPTION WHEN unique_violation THEN
            -- ne rien faire, et tente de nouveau la mise à jour
        END;
    END LOOP;
END;
$$
LANGUAGE plpgsql;
SELECT fusionne_base(1, 'david');
SELECT fusionne_base(1, 'dennis');
     
     Ce code suppose que l'erreur unique_violation est causée
     par la commande INSERT, et pas par un
     INSERT dans une fonction trigger sur la table. Cela
     pourrait avoir un mauvais comportement s'il y a plus d'un index unique sur
     la table car il ré-essaiera l'opération quelque soit l'index qui a causé
     l'erreur. On pourrait avoir plus de sécurité en utilisant la fonctionnalité
     discuté ci-après pour vérifier que l'erreur récupérée était celle attendue.
    
     Les gestionnaires d'exception ont fréquemment besoin d'identifier l'erreur
     spécifique qui est survenue. Il existe deux façons d'obtenir l'information
     sur l'exception en cours dans PL/pgSQL :
     des variables spéciales et la commande GET STACKED
      DIAGNOSTICS.
    
     Avec un gestionnaire d'exceptions, la variable spéciale
     SQLSTATE contient le code d'erreur qui correspond à
     l'exception qui a été levée (voir Tableau A.1 pour la
     liste de codes d'erreur possibles). La variable spéciale
     SQLERRM contient le message d'erreur associé à
     l'exception. Ces variables ne sont pas définies en dehors des gestionnaires
     d'exception.
    
     Dans le gestionnaire d'exceptions, il est possible de récupérer des
     informations sur l'exception en cours en utilisant la commande
     GET STACKED DIAGNOSTICS qui a la forme :
     
GET STACKED DIAGNOSTICSvariable{ = | := }élément[ , ... ];
     Chaque élément est un mot clé identifiant une
     valeur de statut à assigner à la variable spécifiée (qui doit être du bon
     type de données). Les éléments de statut actuellement disponibles sont
     indiqués dans Tableau 42.2.
    
Tableau 42.2. Diagnostics et erreurs
| Nom | Type | Description | 
|---|---|---|
| RETURNED_SQLSTATE | text | le code d'erreur SQLSTATE de l'exception | 
| COLUMN_NAME | text | le nom de la colonne en relation avec l'exception | 
| CONSTRAINT_NAME | text | le nom de la contrainte en relation avec l'exception | 
| PG_DATATYPE_NAME | text | le nom du type de données en relation avec l'exception | 
| MESSAGE_TEXT | text | le texte du message principal de l'exception | 
| TABLE_NAME | text | le nom de la table en relation avec l'exception | 
| SCHEMA_NAME | text | le nom du schéma en relation avec l'exception | 
| PG_EXCEPTION_DETAIL | text | le texte du message détaillée de l'exception, si disponible | 
| PG_EXCEPTION_HINT | text | le texte du message d'astuce de l'exception, si disponible | 
| PG_EXCEPTION_CONTEXT | text | ligne(s) de texte décrivant la pile d'appel au moment de l'exception (voir Section 42.6.9) | 
Si l'exception n'a pas configuré une valeur pour un élément, une chaîne vide sera renvoyée.
Voici un exemple :
DECLARE
  text_var1 text;
  text_var2 text;
  text_var3 text;
BEGIN
  -- un traitement qui cause une exception
  ...
EXCEPTION WHEN OTHERS THEN
  GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,
                          text_var2 = PG_EXCEPTION_DETAIL,
                          text_var3 = PG_EXCEPTION_HINT;
END;
     
    La commande GET DIAGNOSTICS, précédemment décrite dans
    Section 42.5.5, récupère des informations
    sur l'état d'exécution courant (alors que la commande GET STACKED
     DIAGNOSTICS discutée ci-dessus rapporte des informations sur
    l'état d'exécution de l'erreur précédente). Son élément de statut
    PG_CONTEXT est utile pour vérifier l'emplacement
    d'exécution courant. PG_CONTEXT renvoie une chaîne de
    texte dont les lignes correspondent à la pile d'appels. La première ligne
    fait référence à la fonction en cours et qui exécute GET
     DIAGNOSTICS. La seconde ligne et toutes les lignes suivantes font
    référence aux fonctions appelantes dans la pile d'appel. Par exemple :
    
CREATE OR REPLACE FUNCTION fonction_externe() RETURNS integer AS $$BEGIN
  RETURN fonction_interne();
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION fonction_interne() RETURNS integer AS $$
DECLARE
  stack text;
BEGIN
  GET DIAGNOSTICS stack = PG_CONTEXT;
  RAISE NOTICE E'--- Pile d''appel ---\n%', stack;
  RETURN 1;
END;
$$ LANGUAGE plpgsql;
SELECT fonction_externe();
NOTICE:  --- Call Stack ---
PL/pgSQL function fonction_interne() line 5 at GET DIAGNOSTICS
PL/pgSQL function fonction_externe() line 3 at RETURN
CONTEXT:  PL/pgSQL function fonction_externe() line 3 at RETURN
 fonction_externe
 -----------------
                1
(1 row)
    
    GET STACKED DIAGNOSTICS ... PG_EXCEPTION_CONTEXT
    renvoie le même type de pile d'appels, mais en décrivant l'emplacement où
    l'erreur a été détectée, plutôt que l'emplacement actuel.