Formation SQL3
Dalibo SCOP
8 janvier 2018
Vous êtes libres de redistribuer et/ou modifier cette création selon les conditions suivantes :
Paternité
Pas d'utilisation commerciale
Partage des conditions initiales à l'identique
Apparitions de la communauté internationale
OVER)'EXPLAINUNIQUE différableGRANT ALLWHENpg_basebackup utilisable sur un esclaveLATERAL dans un SELECTCOPY FREEZEBRININSERT ... ON CONFLICT { UPDATE | IGNORE }SKIP LOCKEDSQL/MED
pg_stat_statements, ajout de pg_stat_sslGROUPING SETS, CUBE et ROLLUP)VACUUM FREEZE, CHECKPOINT, ancien snapshotSAVEPOINT disponible pour sauvegarde des modifications d'une transaction à un instant tBEGIN ISOLATION LEVEL xxx;read commitedrepeatable readserializableCréation de types de données et
pg_hba.confLes algorithmes suivants sont supportés :
B-tree (par défaut)GiST / SP-GiSTHashGIN (version 8.2)BRIN (version 9.5)CHECK : prix > 0NOT NULL : id_client NOT NULLid_client UNIQUEUNIQUE NOT NULL ==> PRIMARY KEY (id_client)produit_id REFERENCES produits(id_produit)EXCLUDE : EXCLUDE USING gist (room WITH =, during WITH &&)INSERT, COPY, UPDATE, DELETETRUNCATEFOR STATEMENT)FOR EACH ROW)Site de petites annonces :
N'hésitez pas, c'est le moment !
Les langages PL fournissent :
Comment vérifier la présence de la bibliothèque :
find $(pg_config --libdir) -name "plpgsql.so"find $(pg_config --pkglibdir) -name "plpgsql.so"Activer :
CREATE EXTENSION plpgsql;Désactiver :
DROP EXTENSION plpgsql;
pg_language\dxCREATE FUNCTIONPréciser les arguments :
[ [ mode_argument ] [ nom_argument ] type_argument
[ { DEFAULT | = } expr_defaut ] [, ...] ]Il faut aussi indiquer un type de retour :
RETURNS type_retSETOFLe langage de la fonction doit être précisé :
LANGUAGE nomlangMode de la fonction :
IMMUTABLE | STABLE | VOLATILEPrécision sur la façon dont la fonction gère les valeurs NULL :
CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICTCALLED ON NULL INPUT : fonction appelée même si certains arguments sont NULL.RETURNS NULL ON NULL INPUT ou STRICT : la fonction renvoie NULL à chaque fois qu'au moins un argument est NULL.Précision sur la politique de sécurité :
[EXTERNAL] SECURITY INVOKER | [EXTERNAL] SECURITY DEFINERPrécision du code à exécuter :
AS 'definition' | AS 'fichier_obj', 'symbole_lien'fichier_obj est le nom de la bibliothèque, symbole_lien est le nom de la fonction dans le code source CParamètre obsolète :
WITH ( attribut [, ...] )isStrict, équivalent à STRICT ou RETURNS NULL ON NULL INPUTisCachable, équivalent à IMMUTABLECOST cout_execution
ROWS nb_lignes_resultat
PARALLEL [UNSAFE | RESTRICTED | SAFE]
CREATE FUNCTION addition(entier1 integer, entier2 integer)
RETURNS integer
LANGUAGE plpgsql
IMMUTABLE
AS '
DECLARE
  resultat integer;
BEGIN
  resultat := entier1 + entier2;
  RETURN resultat;
END';CREATE OR REPLACE FUNCTIONDROP FUNCTIONArguments (en entrée) nécessaires à l'identification de la fonction à supprimer :
DROP FUNCTION addition(integer, integer);
DROP FUNCTION public.addition(integer, integer);$$ à la place des guillemets qui entourent les sources.ALIAS FOR dans la partie DECLAREVariables déclarées dans le source, dans la partie DECLARE :
DECLARE
nombre integer;
contenu text;Les variables peuvent se voir associées une valeur initiale :
nombre integer := 5;Clause supplémentaire CONSTANT :
DECLARE
valeur_fixe CONSTANT integer := 12;
version_fonction CONSTANT text := '1.12';Possible de récupérer le type d'une autre variable avec %TYPE :
quantite integer;
total quantite%TYPEPossible de récupérer le type de la colonne d'une table :
quantite ma_table.ma_colonne%TYPEUtiliser un type composite :
CREATE TYPE ma_structure AS (un_entier integer,
une_chaine text,
...);
CREATE FUNCTION ma_fonction ()
RETURNS ma_structure...;Possible d'utiliser le type composite défini par la ligne d'une table :
CREATE FUNCTION ma_fonction () RETURNS integer
AS '
DECLARE
ligne ma_table%ROWTYPE;
...CREATE FUNCTION ma_fonction () RETURNS integer
AS '
DECLARE
  ligne RECORD;
  ...
BEGIN
  SELECT INTO ligne * FROM ma_premiere_table;
  -- traitement de la ligne
  FOR ligne IN SELECT * FROM ma_deuxieme_table LOOP
  -- traitement de cette nouvelle ligne
  ...IN : en entréeOUT : en sortieINOUT : en entrée et en sortieRETURN ou RETURN NEXT dans une fonction avec paramètre(s) OUTSELECT INTO :
SELECT INTO un_entier 5;Opérateur := :
un_entier := 5;
un_entier := une_colonne FROM ma_table WHERE id = 5;Utilisation de PERFORM :
PERFORM * FROM ma_table WHERE une_colonne>0;FOUND si une ligne est affectée par l'instructionAffectation de la ligne renvoyée dans une variable de type RECORD ou ROW :
SELECT * INTO ma_variable_ligne FROM ma_table...;SETOFRETURN NEXTExemple :
CREATE FUNCTION liste_entier (limite integer)
RETURNS SETOF integer
AS $$
BEGIN
  FOR i IN 1..limite LOOP
    RETURN NEXT i;
  END LOOP;
END
$$ LANGUAGE plpgsql;Utilisation de cette requête :
ma_base=# SELECT * FROM liste_entier(5);
 
liste_entier
--------------
1
2
3
4
5
(5 lignes)PERFORM <query>INSERT, UPDATE, DELETE (si la clause RETURNING n'est pas utilisée)SELECT, si le résultat importe peuPour obtenir le nombre de lignes affectées :
GET DIAGNOSTICS variable = ROW_COUNT;Instruction :
EXECUTE '<chaine>' [INTO [STRICT] cible];STRICT, cible contient la première ligne d'un résultat multi-lignes ou NULL s'il n'y a pas de résultat.STRICT, une exception est levée si le résultat ne contient aucune ligne (NO_DATA_FOUND) ou en contient plusieurs (TOO_MANY_ROWS).quote_ident pour mettre entre guillemets un identifiant d'un objet PostgreSQL (table, colonne, etc.)quote_literal pour mettre entre guillemets une valeur (chaîne de caractères)quote_nullable pour mettre entre guillemets une valeur (chaîne de caractères), sauf NULL qui sera alors renvoyé sans les guillemetsformat(...), équivalent de sprintf et disponible depuis 9.1Beaucoup plus lisible que des quote_nullable :
EXECUTE 'SELECT count(*) FROM mytable
WHERE inserted_by = $1 AND inserted <= $2'
INTO c
USING checked_user, checked_date;IF condition THEN
  instructions
[ELSEIF condition THEN
 instructions]
[ELSEIF condition THEN
  instructions]
[ELSE
  instructions]
END IFExemple :
IF nombre = 0 THEN
  resultat := 'zero';
ELSEIF nombre > 0 THEN
   resultat := 'positif';
ELSEIF nombre < 0 THEN
   resultat := 'négatif';
ELSE
   resultat := 'indéterminé';
END IF;Deux possibilités :
1ère :
CASE variable
WHEN expression THEN instructions
ELSE instructions
END CASE2nde :
CASE
WHEN expression-booléene THEN instructions
ELSE instructions
END CASELOOP / END LOOP :EXIT [label] [WHEN expression_booléenne]CONTINUE [label] [WHEN expression_booléenne]Exemple :
LOOP
  resultat := resultat + 1;
  EXIT WHEN resultat > 100;
  CONTINUE WHEN resultat < 50;
  resultat := resultat + 1;
END LOOP;Instruction :
WHILE condition LOOP instructions END LOOP;Synopsys :
FOR variable in [REVERSE] entier1..entier2 [BY incrément]
LOOP
instructions
END LOOP;variable va obtenir les différentes valeurs entre entier1 et entier2L'option BY permet d'augmenter l'incrémentation :
FOR variable in 1..10 BY 5...L'option REVERSE permet de faire défiler les valeurs en ordre inverse :
FOR variable in REVERSE 10..1 ...Exemple :
FOR ligne IN SELECT * FROM ma_table LOOP
  instructions
END LOOP;ligne de type RECORD, ROW ou liste de variables séparées par des virgulesSyntaxe :
FOREACH variable [SLICE n] IN ARRAY expression LOOP
    instructions
END LOOPvariable va obtenir les différentes valeurs du tableau retourné par expressionSLICE permet de jouer sur le nombre de dimensions du tableau à passer à la variableRETURN [expression]expression optionnelle si argument(s) déclarés OUT
RETURN lui-même optionnel si argument(s) déclarés OUTSETOF, aussi appelé fonction SRF (Set Returning Function)RETURN NEXT renvoie une ligne du SETOFCette fonction s'appelle de cette façon :
SELECT * FROM ma_fonction();expression de renvoi optionnelle si argument de mode OUTRETURN NEXTRETURN QUERY la_requeteRETURN QUERY EXECUTE chaine_requeteN'hésitez pas, c'est le moment !
Objectifs :
Récupérer le minimum d'une liste :
CREATE FUNCTION pluspetit(VARIADIC numeric[])
RETURNS numeric AS $$
SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;
SELECT pluspetit(10, -1, 5, 4.4);
 pluspetit
-----------
        -1
(1 row)L'addition est un exemple fréquent :
CREATE OR REPLACE FUNCTION
  addition(var1 anyelement, var2 anyelement)
RETURNS anyelement
AS $$
DECLARE
  somme ALIAS FOR $0;
BEGIN
  somme := var1 + var2;
  RETURN somme;
END;
$$ LANGUAGE plpgsql;# SELECT addition(1, 3);
 addition
----------
        4
(1 row)
# SELECT addition(1.3, 3.5);
 addition
----------
      4.8
(1 row)# select addition('un'::text, 'mot'::text);
ERREUR:  L'opérateur n'existe pas : text + text
LIGNE 1 : SELECT   $1  +  $2
^
ASTUCE : Aucun opérateur correspond au nom donné et aux types d'arguments.
    Vous devez ajouter des conversions explicites de type.
REQUÊTE : SELECT   $1  +  $2
CONTEXTE : PL/pgSQL function "addition" line 4 at assignmentOLD :
RECORD correspondant à la ligne avant modificationDELETE et un UPDATENEW :
RECORD correspondant à la ligne après modificationINSERT et un UPDATENEW.champ1 pour accéder à la nouvelle valeur de champ1TG_NAME : nom du trigger qui a déclenché l'appel de la fonctionTG_WHEN : chaîne valant BEFORE, AFTER ou INSTEAD OF suivant le type du triggerTG_LEVEL : chaîne valant ROW ou STATEMENT suivant le mode du tiggerTG_OP : chaîne valant INSERT, UPDATE, DELETE, TRUNCATE suivant l'opération qui a déclenché le triggerTG_RELID : OID de la table qui a déclenché le triggerTG_TABLE_NAME : nom de la table qui a déclenché le triggerTG_TABLE_SCHEMA : nom du schéma contenant la table qui a déclenché le triggerTG_NARGS : nombre d'arguments donnés à la fonction triggerTG_ARGV : les arguments donnés à la fonction trigger (le tableau commence à 0)triggerROW, BEFORE :
ROW, AFTER : valeur de retour ignoréeSTATEMENT : valeur de retour ignoréeCREATE TABLE ma_table (
id serial,
-- un certain nombre de champs informatifs
date_ajout timestamp,
date_modif timestamp);CREATE OR REPLACE FUNCTION horodatage() RETURNS trigger
AS $$
BEGIN
  IF TG_OP = 'INSERT' THEN
    NEW.date_ajout := now();
  ELSEIF TG_OP = 'UPDATE' THEN
    NEW.date_modif := now();
  END IF;
  RETURN NEW;
END; $$ LANGUAGE plpgsql;CREATE TRIGGER permet quelques variantes :
REFERENCING OLD TABLEREFERENCING NEW TABLEPar exemple
CREATE TRIGGER tr1
AFTER DELETE ON t1
REFERENCING OLD TABLE AS oldtable
FOR EACH STATEMENT
EXECUTE PROCEDURE log_delete();REFERENCING OLD TABLEREFERENCING NEW TABLEPar exemple
CREATE TRIGGER tr1
  AFTER DELETE ON t1
  REFERENCING OLD TABLE AS oldtable
  FOR EACH STATEMENT
  EXECUTE PROCEDURE log_delete();Avec le type refcursor :
curseur refcursor;Avec la pseudo-instruction CURSOR FOR :
curseur CURSOR FOR SELECT * FROM ma_table;Avec une requête paramétrée :
curseur CURSOR (param integer) IS
SELECT * FROM ma_table WHERE un_champ=param;Lier une requête à un curseur :
OPEN curseur FOR requeteLier une requête dynamique à un curseur
OPEN curseur FOR EXECUTE chaine_requeteOPEN curseur(arguments)curseur CURSOR FOR SELECT * FROM ma_table;
...
OPEN curseur;Instruction SQL :
FETCH [ direction { FROM | IN } ] curseur INTO cibledirection du FETCH :
Mise à jour d'une ligne d'un curseur :
UPDATE une_table SET ... WHERE CURRENT OF curseurSuppression d'une ligne d'un curseur :
DELETE FROM une_table WHERE CURRENT OF curseurCLOSE curseurrefcursorSans exceptions :
DECLARE
  -- déclaration des variables locales
BEGIN
  -- instructions de la fonction
EXCEPTION
WHEN condition THEN
  -- instructions traitant cette erreur
WHEN condition THEN
  -- autres instructions traitant cette autre erreur
  -- etc.
ENDBEGININSERT, UPDATE, DELETE de la fonction ont été annuléesSQLSTATE : code d'erreurSQLERRM : message d'erreurData Exception : division par zéro, overflow, argument invalide pour certaines fonctions, etc.Integrity Constraint Violation : unicité, CHECK, clé étrangère, etc.Syntax ErrorPL/pgsql Error : RAISE EXCEPTION, pas de données, trop de lignes, etc.RAISE niveau messageDEBUG, LOG, INFO, NOTICE, WARNING, EXCEPTIONExemples :
RAISE WARNING 'valeur % interdite', valeur;RAISE WARNING 'valeur % ambigue',
               valeur
               USING HINT = 'Controlez la valeur saisie en amont';log_min_messages
client_min_messages
RAISE NOTICE message, il faut avoir soit log_min_messages, soit client_min_messages, soit les deux à la valeur NOTICE au minimum.RAISE EXCEPTION messageEXCEPTION gérant la condition RAISE_EXCEPTIONExemple :
RAISE EXCEPTION 'erreur interne';
-- La chose à ne pas faire !Les exceptions non traitées «remontent»
EXECUTEGRANT :GRANT { EXECUTE | ALL [ PRIVILEGES ] }
    ON { FUNCTION function_name [ ( [ [ argmode ] [ arg_name ]
                                                                            arg_type [, ...] ] ) ] [, ...]
         | ALL FUNCTIONS IN SCHEMA schema_name [, ...] }
    TO role_specification [, ...] [ WITH GRANT OPTION ]REVOKE :REVOKE [ GRANT OPTION FOR ]
    { EXECUTE | ALL [ PRIVILEGES ] }
    ON { FUNCTION function_name [ ( [ [ argmode ] [ arg_name ]
                                            arg_type [, ...] ] ) ] [, ...]
         | ALL FUNCTIONS IN SCHEMA schema_name [, ...] }
    FROM { [ GROUP ] role_name | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]SECURITY INVOKER : la fonction s'exécute avec les droits de l'utilisateur qui l'exécuteSECURITY DEFINER : la fonction s'exécute avec les droits de l'utilisateur qui en est le propriétaire
sudo UnixSECURITY DEFINERLEAKPROOF : indique au planificateur que la fonction ne peut pas faire fuiter d'information de contexte
security_barrier# SELECT proargnames, prosrc
FROM pg_proc WHERE proname='addition';
-[ RECORD 1 ]--------------------------
proargnames | {var1,var2}
prosrc      |
            :   DECLARE
            :     somme ALIAS FOR $0;
            :   BEGIN
            :     somme := var1 + var2;
            :     RETURN somme;
            :   END;
            :quote_ident, quote_literal et quote_nullableVOLATILEvolatile : Fonction dont l'exécution ne peut ni ne doit être évitéeimmutable : Fonctions déterministes, dont le résultat peut être précalculé avant de planifier la requête.stable : Fonction ayant un comportement stable au sein d'un même ordre SQL.STRICTLes requêtes statiques :
Les requêtes dynamiques :
Alors, statique ou dynamique ?
shared_preload_libraries
shared_preload_libraries = 'plugin_debugger'Installer l'extension pldbgapi :
mabase# CREATE EXTENSION pldbgapi;make USE_PGXS=1 && make USE_PGXS=1 installshared_preload_libraries = 'log_functions'custom_variable_classesgrep très utile« relation with OID XXXX does not exist »
N'hésitez pas, c'est le moment !