Cette formation est sous licence CC-BY-NC-SA.
Vous êtes libre de la redistribuer et/ou modifier aux conditions
suivantes :
Paternité
Pas d’utilisation commerciale
Partage des conditions initiales à l’identique
Marques déposées
PostgreSQL® Postgres® et le logo Slonik sont des marques
déposées par PostgreSQL Community Association of Canada.
Versions de
PostgreSQL couvertes
Ce document ne couvre que les versions supportées de PostgreSQL au
moment de sa rédaction, soit les versions 13 à 17.
PL/pgSQL avancé
Préambule
Au menu
Routines « variadic » et polymorphes
Fonctions trigger
Curseurs
Récupérer les erreurs
Messages d’erreur dans les logs
Sécurité
Optimisation
Problèmes fréquents
Objectifs
Connaître la majorité des possibilités de PL/pgSQL
Les utiliser pour étendre les fonctionnalités de la base
Écrire du code robuste
Éviter les pièges de sécurité
Savoir optimiser une routine
Routines variadic
Routines variadic :
introduction
Permet de créer des routines avec un nombre d’arguments
variables
… mais du même type
Routines variadic : exemple
Récupérer le minimum d’une liste :
CREATE FUNCTION pluspetit(VARIADICnumeric[])RETURNSnumericAS$$SELECTmin($1[i]) FROM generate_subscripts($1, 1) g(i);$$ LANGUAGE SQL;SELECT pluspetit(10, -1, 5, 4.4); pluspetit----------- -1(1 row)
Routines variadic : exemple
PL/pgSQL
En PL/pgSQL, cette fois-ci
Démonstration de FOREACH xxx IN ARRAY aaa LOOP
Précédemment, obligé de convertir le tableau en relation pour
boucler (unnest)
Routines polymorphes
Routines polymorphes :
introduction
Typer les variables oblige à dupliquer les routines communes à
plusieurs types
PostgreSQL propose des types polymorphes
Le typage se fait à l’exécution
Routines polymorphes :
anyelement
Remplace tout type de données simple ou composite
pour les paramètres en entrée comme pour les paramètres en
sortie
Tous les paramètres et type de retour de type
anyelement se voient attribués le même type
Donc un seul type pour tous les anyelement
autorisés
Paramètre spécial $0 : du type attribué aux éléments
anyelement
Routines polymorphes :
anyarray
anyarray remplace tout tableau de type de données
simple ou composite
pour les paramètres en entrée comme pour les paramètres en
sortie
Le typage se fait à l’exécution
Tous les paramètres de type anyarray se voient
attribués le même type
Routines polymorphes :
exemple
L’addition est un exemple fréquent :
CREATE OR REPLACE FUNCTION addition(var1 anyelement, var2 anyelement)RETURNS anyelementAS$$DECLARE somme ALIAS FOR $0;BEGIN somme := var1 + var2;RETURN somme;END;$$LANGUAGE plpgsql;
FETCH [ direction { FROM|IN } ] curseur INTO cible
Récupère la prochaine ligne
FOUND indique si cette nouvelle ligne a été récupérée
Cible est
une variable RECORD
une variable ROW
un ensemble de variables séparées par des virgules
Curseurs : récupération des
données
direction du FETCH :
NEXT, PRIOR
FIRST, LAST
ABSOLUTE nombre, RELATIVE nombre
nombre
ALL
FORWARD, FORWARD nombre,
FORWARD ALL
BACKWARD, BACKWARD nombre,
BACKWARD ALL
Curseurs : modification des
données
Mise à jour d’une ligne d’un curseur :
UPDATE une_table SET...WHERECURRENTOF curseur;
Suppression d’une ligne d’un curseur :
DELETEFROM une_tableWHERECURRENTOF curseur;
Curseurs : fermeture d’un
curseur
Instruction SQL : CLOSE curseur
Ferme le curseur
Permet de récupérer de la mémoire
Permet aussi de réouvrir le curseur
Curseurs : renvoi d’un curseur
Fonction renvoyant une valeur de type refcursor
Permet donc de renvoyer plusieurs valeurs
Gestion des erreurs
Gestion des erreurs :
introduction
Sans exceptions :
toute erreur provoque un arrêt de la fonction
toute modification suite à une instruction SQL (INSERT,
UPDATE, DELETE) est annulée
d’où l’ajout d’une gestion personnalisée des erreurs avec le concept
des exceptions
Gestion des erreurs : une
exception
La fonction comporte un bloc supplémentaire,
EXCEPTION :
DECLARE-- déclaration des variables localesBEGIN-- instructions de la fonctionEXCEPTIONWHENconditionTHEN-- instructions traitant cette erreurWHENconditionTHEN-- autres instructions traitant cette autre erreur-- etc.END
Gestion des
erreurs : flot dans une fonction
L’exécution de la fonction commence après le BEGIN
Si aucune erreur ne survient, le bloc EXCEPTION est
ignoré
Si une erreur se produit
tout ce qui a été modifié dans la base dans le bloc est annulé
les variables gardent par contre leur état
l’exécution passe directement dans le bloc de gestion de
l’exception
Gestion des
erreurs : flot dans une exception
Recherche d’une condition satisfaisante
Si cette condition est trouvée
exécution des instructions correspondantes
Si aucune condition n’est compatible
sortie du bloc BEGIN/END comme si le bloc
d’exception n’existait pas
passage de l’exception au bloc BEGIN/END
contenant (après annulation de ce que ce bloc a modifié en base)
Dans un bloc d’exception, les instructions INSERT,
UPDATE, DELETE de la fonction ont été
annulées
Dans un bloc d’exception, les variables locales de la fonction ont
gardé leur ancienne valeur
Gestion des erreurs : codes
d’erreurs
SQLSTATE : code d’erreur
SQLERRM : message d’erreur
Par exemple :
Data Exception : division par zéro, overflow, argument
invalide pour certaines fonctions, etc.
Integrity Constraint Violation : unicité, CHECK, clé
étrangère, etc.
Syntax Error
PL/pgSQL Error : RAISE EXCEPTION, pas de
données, trop de lignes, etc.
Les erreurs sont contenues dans des classes d’erreurs plus
génériques, qui peuvent aussi être utilisées
Messages d’erreurs : RAISE - 1
Envoyer une trace dans les journaux applicatifs et/ou vers le client
RAISE niveau message
Niveau correspond au niveau d’importance du message
DEBUG, LOG, INFO,
NOTICE, WARNING, EXCEPTION
Message est la trace à enregistrer
Message dynamique… tout signe % est remplacé par la
valeur indiquée après le message
Champs DETAIL et HINT disponibles
Messages d’erreurs : RAISE - 2
Exemples :
RAISE WARNING 'valeur % interdite', valeur;
RAISE WARNING 'valeur % ambigue', valeurUSING HINT ='Controlez la valeur saisie en amont';
Messages d’erreurs :
configuration des logs
Deux paramètres importants pour les traces
log_min_messages
niveau minimum pour que la trace soit enregistrée dans les
journaux
client_min_messages
niveau minimum pour que la trace soit envoyée au client
Dans le cas d’un RAISE NOTICE message, il faut avoir
soit log_min_messages, soit
client_min_messages, soit les deux à la valeur
NOTICE au minimum.
Messages d’erreurs :
RAISE EXCEPTION - 1
Annule le bloc en cours d’exécution
RAISE EXCEPTION message
Sauf en cas de présence d’un bloc EXCEPTION gérant la
condition RAISE_EXCEPTION
message est la trace à enregistrer, et est dynamique… tout signe %
est remplacé par la valeur indiquée après le message
Messages d’erreurs :
RAISE EXCEPTION - 2
Exemple :
RAISE EXCEPTION'erreur interne';-- La chose à ne pas faire !
Flux des erreurs dans du code
PL
Les exceptions non traitées «remontent»
de bloc BEGIN/END imbriqués vers les blocs
parents (fonctions appelantes comprises)
jusqu’à ce que personne ne puisse les traiter
voir note pour démonstration
Flux des erreurs dans du
code PL - 2
Les erreurs remontent
Cette fois-ci, on rajoute un bloc PL pour intercepter l’erreur
Flux des erreurs dans du
code PL - 3
Cette fois-ci, on rajoute un bloc PL indépendant pour gérer le
second INSERT
Flux des erreurs dans du
code PL - 4
Illustrons maintenant la remontée d’erreurs
Nous avons deux blocs imbriqués
Une erreur non prévue va se produire dans le bloc intérieur
Abus des exceptions
Ne pas abuser des exceptions
les sous-transactions ne sont pas gratuites
Si saturation, paramétre SLRU dédié (v17)
Sécurité
Sécurité : droits
L’exécution de la routine dépend du droit EXECUTE
Par défaut, ce droit est donné à la création de la routine
au propriétaire de la routine
au groupe spécial PUBLIC
Sécurité : ajout
Ce droit peut être donné avec l’instruction SQL
GRANT :