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
)'EXPLAIN
UNIQUE
différableGRANT ALL
WHEN
pg_basebackup
utilisable sur un esclaveLATERAL
dans un SELECT
COPY FREEZE
BRIN
INSERT ... ON CONFLICT { UPDATE | IGNORE }
SKIP LOCKED
SQL/MED
pg_stat_statements
, ajout de pg_stat_ssl
GROUPING SETS
, CUBE
et ROLLUP
)VACUUM FREEZE
, CHECKPOINT
, ancien snapshotSAVEPOINT
disponible pour sauvegarde des modifications d'une transaction à un instant t
BEGIN ISOLATION LEVEL xxx;
read commited
repeatable read
serializable
Création de types de données et
pg_hba.conf
Les algorithmes suivants sont supportés :
B-tree
(par défaut)GiST
/ SP-GiST
Hash
GIN
(version 8.2
)BRIN
(version 9.5
)CHECK
: prix > 0
NOT NULL
: id_client NOT NULL
id_client UNIQUE
UNIQUE NOT NULL
==> PRIMARY KEY (id_client)
produit_id REFERENCES produits(id_produit)
EXCLUDE
: EXCLUDE USING gist (room WITH =, during WITH &&)
INSERT
, COPY
, UPDATE
, DELETE
TRUNCATE
FOR 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
\dx
CREATE FUNCTION
Préciser les arguments :
[ [ mode_argument ] [ nom_argument ] type_argument
[ { DEFAULT | = } expr_defaut ] [, ...] ]
Il faut aussi indiquer un type de retour :
RETURNS type_ret
SETOF
Le langage de la fonction doit être précisé :
LANGUAGE nomlang
Mode de la fonction :
IMMUTABLE | STABLE | VOLATILE
Précision sur la façon dont la fonction gère les valeurs NULL :
CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
CALLED 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 DEFINER
Pré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 INPUT
isCachable
, équivalent à IMMUTABLE
COST 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 FUNCTION
DROP FUNCTION
Arguments (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 DECLARE
Variables 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%TYPE
Possible de récupérer le type de la colonne d'une table :
quantite ma_table.ma_colonne%TYPE
Utiliser 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) OUT
SELECT 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...;
SETOF
RETURN NEXT
Exemple :
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 IF
Exemple :
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 CASE
2nde :
CASE
WHEN expression-booléene THEN instructions
ELSE instructions
END CASE
LOOP
/ 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 LOOP
variable
va obtenir les différentes valeurs du tableau retourné par expression
SLICE
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 SETOF
Cette fonction s'appelle de cette façon :
SELECT * FROM ma_fonction();
expression
de renvoi optionnelle si argument de mode OUTRETURN NEXT
RETURN QUERY la_requete
RETURN QUERY EXECUTE chaine_requete
N'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 assignment
OLD
:
RECORD
correspondant à la ligne avant modificationDELETE
et un UPDATE
NEW
:
RECORD
correspondant à la ligne après modificationINSERT
et un UPDATE
NEW.champ1
pour accéder à la nouvelle valeur de champ1
TG_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)trigger
ROW
, 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 TABLE
REFERENCING NEW TABLE
Par exemple
CREATE TRIGGER tr1
AFTER DELETE ON t1
REFERENCING OLD TABLE AS oldtable
FOR EACH STATEMENT
EXECUTE PROCEDURE log_delete();
REFERENCING OLD TABLE
REFERENCING NEW TABLE
Par 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 requete
Lier une requête dynamique à un curseur
OPEN curseur FOR EXECUTE chaine_requete
OPEN curseur(arguments)
curseur CURSOR FOR SELECT * FROM ma_table;
...
OPEN curseur;
Instruction SQL :
FETCH [ direction { FROM | IN } ] curseur INTO cible
direction
du FETCH
:
Mise à jour d'une ligne d'un curseur :
UPDATE une_table SET ... WHERE CURRENT OF curseur
Suppression d'une ligne d'un curseur :
DELETE FROM une_table WHERE CURRENT OF curseur
CLOSE curseur
refcursor
Sans 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.
END
BEGIN
INSERT
, 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 Error
PL/pgsql Error
: RAISE EXCEPTION
, pas de données, trop de lignes, etc.RAISE niveau message
DEBUG
, LOG
, INFO
, NOTICE
, WARNING
, EXCEPTION
Exemples :
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 message
EXCEPTION
gérant la condition RAISE_EXCEPTION
Exemple :
RAISE EXCEPTION 'erreur interne';
-- La chose à ne pas faire !
Les exceptions non traitées «remontent»
EXECUTE
GRANT
: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 DEFINER
LEAKPROOF
: 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_nullable
VOLATILE
volatile
: 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.STRICT
Les 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 install
shared_preload_libraries = 'log_functions'
custom_variable_classes
grep
très utile« relation with OID XXXX does not exist »
N'hésitez pas, c'est le moment !