forked from gleu/pgdocs_fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plpgsql.xml
4695 lines (4106 loc) · 187 KB
/
plpgsql.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<chapter id="plpgsql">
<title><application>PL/pgSQL</application> - Langage de procédures <acronym>SQL</acronym></title>
<indexterm zone="plpgsql">
<primary>PL/pgSQL</primary>
</indexterm>
<sect1 id="plpgsql-overview">
<title>Aperçu</title>
<para>
<application>PL/pgSQL</application> est un langage de procédures chargeable
pour le système de bases de données <productname>PostgreSQL</productname>.
Les objectifs de la conception de <application>PL/pgSQL</application> ont été de
créer un langage de procédures chargeable qui
<itemizedlist>
<listitem>
<para>
est utilisé pour créer des fonctions standards et triggers,
</para>
</listitem>
<listitem>
<para>
ajoute des structures de contrôle au langage <acronym>SQL</acronym>,
</para>
</listitem>
<listitem>
<para>
permet d'effectuer des traitements complexes,
</para>
</listitem>
<listitem>
<para>
hérite de tous les types, fonctions et opérateurs définis par les
utilisateurs,
</para>
</listitem>
<listitem>
<para>
est défini comme digne de confiance par le serveur,
</para>
</listitem>
<listitem>
<para>
est facile à utiliser.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Les fonctions <application>PL/pgSQL</application> acceptent un nombre
variable d'arguments en utilisant le marqueur <literal>VARIADIC</literal>.
Cela fonctionne exactement de la même façon pour les fonctions SQL, comme
indiqué dans <xref linkend="xfunc-sql-variadic-functions"/>.
</para>
<para>
Les fonctions écrites en <application>PL/pgSQL</application> peuvent être
utilisées partout où une fonction intégrée peut l'être.
Par exemple, il est possible de créer des fonctions complexes de traitement
conditionnel et, par la suite, de les utiliser pour définir des opérateurs
ou de les utiliser dans des expressions d'index.
</para>
<sect2 id="plpgsql-advantages">
<title>Avantages de l'utilisation de <application>PL/pgSQL</application></title>
<para>
<acronym>SQL</acronym> est le langage que <productname>PostgreSQL</productname>
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 <acronym>SQL</acronym> doit être exécutée individuellement
par le serveur de bases de données.
</para>
<para>
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
et traiter les résultats, faire quelques calculs, et enfin envoyer d'autres requêtes
au serveur. Tout ceci induit des communications interprocessus et induit aussi
une surcharge du réseau si votre client est sur une machine différente
du serveur de bases de données.
</para>
<para>
Grâce à <application>PL/pgSQL</application> vous pouvez grouper un bloc
de traitement et une série de requêtes <emphasis>au sein</emphasis> du
serveur de bases de données, et bénéficier ainsi de la puissance d'un
langage de procédures, mais avec de gros gains en terme de communication
client/serveur.
</para>
<itemizedlist>
<listitem><para>Les allers/retours entre le
client et le serveur sont éliminés</para></listitem>
<listitem><para>Il n'est pas nécessaire de traiter ou transférer entre le
client et le serveur les résultats intermédiaires dont le client n'a pas
besoin</para></listitem>
<listitem><para>Les va-et-vient des analyses de requêtes peuvent être
évités</para></listitem>
</itemizedlist>
<para>
Ceci a pour résultat une augmentation considérable des performances
en comparaison à une application qui n'utilise pas les procédures stockées.
</para>
<para>
Ainsi, avec <application>PL/pgSQL</application> vous pouvez utiliser tous les
types de données, opérateurs et fonctions du SQL.
</para>
</sect2>
<sect2 id="plpgsql-args-results">
<title>Arguments supportés et types de données résultats</title>
<para>
Les fonctions écrites en <application>PL/pgSQL</application> peuvent accepter
en 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
<application>PL/pgSQL</application> renvoyant un type <type>record</type>,
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
<xref linkend="queries-tablefunctions"/>).
</para>
<para>
Les fonctions <application>PL/pgSQL</application> acceptent en entrée et
en sortie les types polymorphes <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type> et <type>anyenum</type>.
Le type de données réel géré par une fonction polymorphe peut varier
d'appel en appel (voir la <xref linkend="extend-types-polymorphic"/>).
Voir l'exemple de la <xref linkend="plpgsql-declaration-aliases"/>.
</para>
<para>
Les fonctions <application>PL/pgSQL</application> peuvent aussi renvoyer
un ensemble de lignes (ou une table) de n'importe lequel des type de
données dont les fonctions peuvent renvoyer une instance unique. Ces
fonctions génèrent leur sortie en exécutant <literal>RETURN NEXT</literal>
pour chaque élément désiré de l'ensemble résultat ou en utilisant
<command>RETURN QUERY</command> pour afficher le résultat de l'évaluation
d'une requête.
</para>
<para>
Enfin, une fonction <application>PL/pgSQL</application> peut être déclarée comme renvoyant
<type>void</type> si elle n'a pas de valeur de retour utile.
</para>
<para>
Les fonctions <application>PL/pgSQL</application> peuvent aussi être déclarées
avec des paramètres en sortie à la place de la spécification explicite
du code de retour. Ceci n'ajoute pas de fonctionnalité fondamentale au
langage mais c'est un moyen agréable principalement pour renvoyer
plusieurs valeurs.
La notation <literal>RETURNS TABLE</literal> peut aussi être utilisé à la
place de <literal>RETURNS SETOF</literal>.
</para>
<para>
Des exemples spécifiques apparaissent dans la
<xref linkend="plpgsql-declaration-aliases"/> et la
<xref linkend="plpgsql-statements-returning"/>.
</para>
</sect2>
</sect1>
<sect1 id="plpgsql-structure">
<title>Structure de <application>PL/pgSQL</application></title>
<para>
<application>PL/pgSQL</application> est un langage structuré en blocs.
Le texte complet de la définition d'une fonction doit être un
<firstterm>bloc</firstterm>. Un bloc est défini comme :
<synopsis><optional> <<<replaceable>label</replaceable>>> </optional>
<optional> DECLARE
<replaceable>déclarations</replaceable> </optional>
BEGIN
<replaceable>instructions</replaceable>
END <optional> <replaceable>label</replaceable> </optional>;
</synopsis>
</para>
<para>
Chaque déclaration et chaque expression au sein du bloc est terminé par un
point-virgule. Un bloc qui apparaît à l'intérieur d'un autre bloc doit avoir
un point-virgule après <literal>END</literal> (voir l'exemple ci-dessus) ;
néanmoins, le <literal>END</literal> final qui conclut le corps d'une fonction
n'a pas besoin de point-virgule.
</para>
<tip>
<para>
Une erreur habituelle est d'écrire un point-virgule immédiatement
après <literal>BEGIN</literal>. C'est incorrect et a comme résultat
une erreur de syntaxe.
</para>
</tip>
<para>
Un <replaceable>label</replaceable> est seulement nécessaire si vous voulez
identifier le bloc à utiliser dans une instruction <literal>EXIT</literal>
ou pour qualifier les noms de variable déclarées dans le bloc. Si un label
est écrit après <literal>END</literal>, il doit correspondre au label donné
au début du bloc.
</para>
<para>
Tous les mots clés sont insensibles à la casse. Les identifiants sont
convertis implicitement en minuscule sauf dans le cas de l'utilisation
de guillemets doubles. Le comportement est donc identique à celui des
commandes SQL habituelles.
</para>
<para>
Il y a deux types de commentaires dans <application>PL/pgSQL</application>. Un double
tiret (<literal>--</literal>) débute une ligne de commentaire qui s'étend
jusqu'à la fin de la ligne. Un <literal>/*</literal> débute un bloc de commentaire
qui s'étend jusqu'à la prochaine occurrence de <literal>*/</literal>.
</para>
<para>
Chaque expression de la section expression d'un bloc peut être un
<firstterm>sous-bloc</firstterm>. Les sous-blocs peuvent être utilisés pour
des groupements logiques ou pour situer des variables locales dans un petit groupe
d'instructions. Les variables déclarées dans un sous-bloc masquent toute
variable nommée de façon similaire dans les blocs externes pendant toute la durée du
sous-bloc. Cependant, vous pouvez accéder aux variables externes si vous
qualifiez leur nom du label de leur bloc. Par exemple :
<programlisting>CREATE FUNCTION une_fonction() RETURNS integer AS $$
<< blocexterne >>
DECLARE
quantite integer := 30;
BEGIN
RAISE NOTICE 'quantité vaut ici %', quantite; -- affiche 30
quantite := 50;
--
-- Crée un sous-bloc
--
DECLARE
quantite integer := 80;
BEGIN
RAISE NOTICE 'quantite vaut ici %', quantite; -- affiche 80
RAISE NOTICE 'la quantité externe vaut ici %', blocexterne.quantite; -- affiche 50
END;
RAISE NOTICE 'quantité vaut ici %', quantite; -- affiche 50
RETURN quantite;
END;
$$ LANGUAGE plpgsql;
</programlisting>
</para>
<note>
<para>
Il existe un bloc externe caché entourant le corps de
toute fonction <application>PL/pgSQL</application>. Ce bloc fournit la
déclaration des paramètres de la fonction ainsi que quelques variables
spéciales comme <literal>FOUND</literal> (voir la
<xref linkend="plpgsql-statements-diagnostics"/>). Le bloc externe a pour
label le nom de la fonction. Cela a pour conséquence que les paramètres et les
variables spéciales peuvent être qualifiés du nom de la fonction.
</para>
</note>
<para>
Il est important de ne pas confondre l'utilisation de
<command>BEGIN</command>/<command>END</command> pour grouper les instructions dans
<application>PL/pgSQL</application> avec les commandes pour le
contrôle des transactions. Les <command>BEGIN</command>/<command>END</command> de
<application>PL/pgSQL</application> ne servent qu'au groupement ; ils ne débutent
ni ne terminent une transaction. Les fonctions standards et les fonctions triggers
sont toujours exécutées à l'intérieur d'une transaction établie par une
requête extérieure — ils ne peuvent pas être utilisés pour commencer
ou valider une transaction car ils n'auraient pas de contexte pour s'exécuter.
Néanmoins, un bloc contenant une clause <literal>EXCEPTION</literal> forme réellement
une sous-transaction qui peut être annulée sans affecter la transaction
externe. Pour plus d'informations sur ce point, voir la <xref
linkend="plpgsql-error-trapping"/>.
</para>
</sect1>
<sect1 id="plpgsql-declarations">
<title>Déclarations</title>
<para>
Toutes les variables utilisées dans un bloc doivent être déclarées dans la
section déclaration du bloc. Les seules exceptions sont que la variable de
boucle d'une boucle <literal>FOR</literal> effectuant une itération sur
des valeurs entières est automatiquement déclarée comme variable entière
(type integer), et de la même façon une variable de boucle
<literal>FOR</literal> effectuant une itération sur le résultat d'un
curseur est automatiquement déclarée comme variable de type
record.
</para>
<para>
Les variables <application>PL/pgSQL</application> peuvent être de n'importe quel type de données
tels que <type>integer</type>, <type>varchar</type> et
<type>char</type>.
</para>
<para>
Quelques exemples de déclaration de variables :
<programlisting>id_utilisateur integer;
quantité numeric(5);
url varchar;
ma_ligne nom_table%ROWTYPE;
mon_champ nom_table.nom_colonne%TYPE;
une_ligne RECORD;
</programlisting>
</para>
<para>
La syntaxe générale d'une déclaration de variable est :
<synopsis><replaceable>nom</replaceable> <optional> CONSTANT </optional> <replaceable>type</replaceable> <optional> NOT NULL </optional> <optional> { DEFAULT | := } <replaceable>expression</replaceable> </optional>;
</synopsis>
La clause <literal>DEFAULT</literal>, si indiquée, spécifie la valeur
initiale affectée à la variable quand on entre dans le bloc.
Si la clause <literal>DEFAULT</literal> n'est pas indiquée, la variable
est initialisée à la valeur <acronym>SQL</acronym> NULL.
L'option <literal>CONSTANT</literal> empêche la modification de la
variable, de sorte que sa valeur reste constante pour la durée du bloc.
Si <literal>NOT NULL</literal> est spécifié, l'affectation d'une valeur
NULL aboutira à une erreur d'exécution. Les valeurs par défaut de toutes
les variables déclarées <literal>NOT NULL</literal> doivent être
précisées, donc non NULL.
</para>
<para>
La valeur par défaut d'une variable est évaluée et affectée à la variable
à chaque entrée du bloc (pas seulement une fois lors de l'appel de la
fonction). Ainsi, par exemple, l'affectation de <literal>now()</literal>
à une variable de type <type>timestamp</type> donnera à la variable
l'heure de l'appel de la fonction courante, et non l'heure au moment où
la fonction a été précompilée.
</para>
<para>
Exemples :
<programlisting>quantité integer DEFAULT 32;
url varchar := 'http://mysite.com';
id_utilisateur CONSTANT integer := 10;
</programlisting>
</para>
<sect2 id="plpgsql-declaration-aliases">
<title>Alias de paramètres de fonctions</title>
<para>
Les paramètres passés aux fonctions sont nommés par les identifiants
<literal>$1</literal>, <literal>$2</literal>,
etc. Éventuellement, des alias peuvent être déclarés pour les noms de paramètres
de type <literal>$<replaceable>n</replaceable></literal> afin d'améliorer la
lisibilité. L'alias ou l'identifiant numérique peuvent être utilisés indifféremment
pour se référer à la valeur du paramètre.
</para>
<para>
Il existe deux façons de créer un alias. La façon préférée est de donner
un nom au paramètre dans la commande <command>CREATE FUNCTION</command>,
par exemple :
<programlisting>CREATE FUNCTION taxe_ventes(sous_total real) RETURNS real AS $$
BEGIN
RETURN sous_total * 0.06;
END;
</programlisting>
L'autre façon, la seule disponible pour les versions antérieures à
<productname>PostgreSQL</productname> 8.0, est de déclarer explicitement
un alias en utilisant la syntaxe de déclaration :
<synopsis><replaceable>nom</replaceable> ALIAS FOR $<replaceable>n</replaceable>;
</synopsis>
Le même exemple dans ce style ressemble à ceci :
<programlisting>CREATE FUNCTION taxe_ventes(real) RETURNS real AS $$
DECLARE
sous_total ALIAS FOR $1;
BEGIN
RETURN sous_total * 0.06;
END;
$$ LANGUAGE plpgsql;
</programlisting>
</para>
<note>
<para>
Ces deux exemples ne sont pas complètement identiques. Dans le premier cas,
<literal>sous_total</literal> peut être référencé comme
<literal>taxe_ventes.sous_total</literal>, alors que ce n'est pas possible
dans le second cas. (Si nous avions attaché un label au bloc,
<literal>sous_total</literal> aurait pu utiliser ce label à la place.)
</para>
</note>
<para>
Quelques exemples de plus :
<programlisting>CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$
DECLARE
v_string ALIAS FOR $1;
index ALIAS FOR $2;
BEGIN
-- quelques traitements utilisant ici v_string et index
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION concat_champs_selectionnes(in_t un_nom_de_table) RETURNS text AS $$
BEGIN
RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
$$ LANGUAGE plpgsql;
</programlisting>
</para>
<para>
Quand une fonction <application>PL/pgSQL</application> est déclarée avec
des paramètres en sortie, ces derniers se voient attribués les noms
<literal>$<replaceable>n</replaceable></literal> et des alias optionnels
de la même façon que les paramètres en entrée. Un paramètre en sortie est
une variable qui commence avec la valeur NULL ; il
devrait se voir attribuer une valeur lors de l'exécution de la fonction.
La valeur finale du paramètre est ce qui est renvoyée. Par exemple,
l'exemple taxe_ventes peut s'écrire de cette façon :
<programlisting>CREATE FUNCTION taxe_ventes(sous_total real, OUT taxe real) AS $$
BEGIN
taxe := sous_total * 0.06;
END;
$$ LANGUAGE plpgsql;
</programlisting>
Notez que nous avons omis <literal>RETURNS real</literal>. Nous aurions
pu l'inclure mais cela aurait été redondant.
</para>
<para>
Les paramètres en sortie sont encore plus utiles lors du retour de
plusieurs valeurs. Un exemple trivial est :
<programlisting>CREATE FUNCTION somme_n_produits(x int, y int, OUT somme int, OUT produit int) AS $$
BEGIN
somme := x + y;
produit := x * y;
END;
$$ LANGUAGE plpgsql;
</programlisting>
D'après ce qui a été vu dans la <xref linkend="xfunc-output-parameters"/>,
ceci crée réellement un type d'enregistrement anonyme pour les résultats
de la fonction. Si une clause <literal>RETURNS</literal> est donnée, elle doit
spécifier <literal>RETURNS record</literal>.
</para>
<para>
Voici une autre façon de déclarer une fonction
<application>PL/pgSQL</application>, cette fois avec <literal>RETURNS
TABLE</literal> :
<programlisting>
CREATE FUNCTION extended_sales(p_itemno int) RETURNS TABLE(quantity int, total numeric) AS $$
BEGIN
RETURN QUERY SELECT quantity, quantity * price FROM sales WHERE itemno = p_itemno;
END;
$$ LANGUAGE plpgsql;
</programlisting>
C'est exactement équivalent à déclarer un ou plusieurs paramètres
<literal>OUT</literal> et à spécifier <literal>RETURNS SETOF
<replaceable>un_type</replaceable></literal>.
</para>
<para>
Lorsque le type de retour d'une fonction <application>PL/pgSQL</application>
est déclaré comme type polymorphe (<type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type> et <type>anyenum</type>), un
paramètre spécial <literal>$0</literal> est créé.
Son type de donnée est le type effectif de retour de la fonction, déduit d'après
les types en entrée (voir la <xref linkend="extend-types-polymorphic"/>).
Ceci permet à la fonction d'accéder à son type de retour réel comme on le voit ici
avec la <xref linkend="plpgsql-declaration-type"/>.
<literal>$0</literal> est initialisé à NULL et peut être modifié par la fonction,
de sorte qu'il peut être utilisé pour contenir la variable de retour si besoin est,
bien que cela ne soit pas requis. On peut aussi donner un alias à
<literal>$0</literal>. Par exemple, cette fonction s'exécute comme un
opérateur <literal>+</literal> pour n'importe quel type de données :
<programlisting>CREATE FUNCTION ajoute_trois_valeurs(v1 anyelement, v2 anyelement, v3 anyelement)
RETURNS anyelement AS $$
DECLARE
resultat ALIAS FOR $0;
BEGIN
resultat := v1 + v2 + v3;
RETURN resultat;
END;
$$ LANGUAGE plpgsql;
</programlisting>
</para>
<para>
Le même effet peut être obtenu en déclarant un ou plusieurs paramètres
polymorphes en sortie de types. Dans ce
cas, le paramètre spécial <literal>$0</literal> n'est pas utilisé ;
les paramètres en sortie servent ce même but. Par exemple :
<programlisting>CREATE FUNCTION ajoute_trois_valeurs(v1 anyelement, v2 anyelement, v3 anyelement,
OUT somme anyelement)
AS $$
BEGIN
somme := v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;
</programlisting>
</para>
</sect2>
<sect2 id="plpgsql-declaration-type">
<title>Copie de types</title>
<synopsis><replaceable>variable</replaceable>%TYPE
</synopsis>
<para>
<literal>%TYPE</literal> fournit le type de données d'une variable ou d'une
colonne de table. Vous pouvez l'utiliser pour déclarer des variables qui
contiendront des valeurs de base de données. Par exemple, disons que vous
avez une colonne nommée <literal>id_utilisateur</literal> dans votre table
<literal>utilisateurs</literal>. Pour déclarer une variable du même type de
données que <literal>utilisateurs.id_utilisateur</literal>, vous pouvez
écrire :
<programlisting>id_utilisateur utilisateurs.id_utilisateur%TYPE;
</programlisting>
</para>
<para>
En utilisant <literal>%TYPE</literal> vous n'avez pas besoin de connaître
le type de données de la structure à laquelle vous faites référence et, plus
important, si le type de données de l'objet référencé change dans le futur (par
exemple : vous changez le type de <literal>id_utilisateur</literal> de
<type>integer</type> à <type>real</type>), vous pouvez ne pas avoir besoin de
changer votre définition de fonction.
</para>
<para>
<literal>%TYPE</literal> est particulièrement utile dans le cas de fonctions
polymorphes puisque les types de données nécessaires aux variables internes
peuvent changer d'un appel à l'autre. Des variables appropriées peuvent être
créées en appliquant <literal>%TYPE</literal> aux arguments de la fonction ou à
la variable fictive de résultat.
</para>
</sect2>
<sect2 id="plpgsql-declaration-rowtypes">
<title>Types ligne</title>
<synopsis><replaceable>nom</replaceable> <replaceable>nom_table</replaceable><literal>%ROWTYPE</literal>;
<replaceable>nom</replaceable> <replaceable>nom_type_composite</replaceable>;
</synopsis>
<para>
Une variable de type composite est appelée variable <firstterm>ligne</firstterm> (ou
variable <firstterm>row-type</firstterm>). Une telle variable peut contenir une ligne entière
de résultat de requête <command>SELECT</command> ou <command>FOR</command>, du moment
que l'ensemble de colonnes de la requête correspond au type déclaré de la variable.
Les champs individuels de la valeur row sont accessibles en utilisant la notation
pointée, par exemple <literal>varligne.champ</literal>.
</para>
<para>
Une variable ligne peut être déclarée de façon à avoir le même type que les lignes
d'une table ou d'une vue existante, en utilisant la notation
<replaceable>nom_table</replaceable><literal>%ROWTYPE</literal>.
Elle peut aussi être déclarée en donnant un nom de type composite. Chaque table
ayant un type de données associé du même nom, il importe peu dans
<productname>PostgreSQL</productname> que vous écriviez <literal>%ROWTYPE</literal> ou pas.
Cependant, la forme utilisant <literal>%ROWTYPE</literal> est plus portable.
</para>
<para>
Les paramètres d'une fonction peuvent être des types composites
(lignes complètes de tables). Dans ce cas, l'identifiant correspondant
<literal>$<replaceable>n</replaceable></literal> sera une variable ligne à partir de laquelle
les champs peuvent être sélectionnés avec la notation pointée, par exemple <literal>$1.id_utilisateur</literal>.
</para>
<para>
Seules les colonnes définies par l'utilisateur sont accessibles
dans une variable de type ligne, et non l'OID ou d'autres colonnes systèmes (parce que
la ligne pourrait être issue d'une vue). Les champs du type ligne héritent des tailles
des champs de la table ou de leur précision pour les types de données tels que
<type>char(<replaceable>n</replaceable>)</type>.
</para>
<para>
Voici un exemple d'utilisation des types composites. <structname>table1</structname>
et <structname>table2</structname> sont des tables ayant au moins les champs
mentionnés :
<programlisting>CREATE FUNCTION assemble_champs(t_ligne table1) RETURNS text AS $$
DECLARE
t2_ligne table2%ROWTYPE;
BEGIN
SELECT * INTO t2_ligne FROM table2 WHERE ... ;
RETURN t_ligne.f1 || t2_ligne.f3 || t_ligne.f5 || t2_ligne.f7;
END;
$$ LANGUAGE plpgsql;
SELECT assemble_champs(t.*) FROM table1 t WHERE ... ;
</programlisting>
</para>
</sect2>
<sect2 id="plpgsql-declaration-records">
<title>Types record</title>
<para>
<synopsis><replaceable>nom</replaceable> RECORD;
</synopsis>
</para>
<para>
Les variables record sont similaires aux variables de type ligne mais n'ont
pas de structure prédéfinie. Elles empruntent la structure effective de
type ligne de la ligne à laquelle elles sont affectées durant une commande
<command>SELECT</command> ou <command>FOR</command>. La sous-structure d'une
variable record peut changer à chaque fois qu'on l'affecte. Une conséquence
de cela est qu'elle n'a pas de sous-structure jusqu'à ce qu'elle ait été
affectée, et toutes les tentatives pour accéder à un de ses champs
entraînent une erreur d'exécution.
</para>
<para>
Notez que <literal>RECORD</literal> n'est pas un vrai type de données mais seulement un
paramètre fictif (placeholder). Il faut aussi réaliser que lorsqu'une fonction
<application>PL/pgSQL</application> est déclarée renvoyer un type <type>record</type>,
il ne s'agit pas tout à fait du même concept qu'une variable record, même si
une telle fonction peut aussi utiliser une variable record pour contenir son
résultat. Dans les deux cas, la structure réelle de la ligne n'est pas connue quand
la fonction est écrite mais, dans le cas d'une fonction renvoyant un type
<type>record</type>, la structure réelle est déterminée quand la requête appelante est
analysée, alors qu'une variable record peut changer sa structure de ligne à la volée.
</para>
</sect2>
<sect2 id="plpgsql-declaration-renaming-vars">
<title><literal>RENAME</literal></title>
<synopsis>RENAME <replaceable>ancien nom</replaceable> TO <replaceable>nouveau nom</replaceable>;
</synopsis>
<para>
En utilisant la déclaration <literal>RENAME</literal>, vous pouvez changer le nom d'une variable,
d'un record ou d'un ligne (ROW). C'est particulièrement utile si
<varname>NEW</varname> ou <varname>OLD</varname> doivent
être référencés par un autre nom dans une procédure trigger. Voir
aussi <literal>ALIAS</literal>.
</para>
<para>
Exemples :
<programlisting>RENAME id TO id_utilisateur;
RENAME cette_var TO cette_autre_var;
</programlisting>
</para>
<note>
<para>
<literal>RENAME</literal> semble ne pas fonctionner dans <productname>PostgreSQL</productname> 7.3. Cette
correction est de faible priorité, <literal>ALIAS</literal> couvrant la plupart des utilisations
pratiques de <literal>RENAME</literal>.
</para>
</note>
</sect2>
</sect1>
<sect1 id="plpgsql-expressions">
<title>Expressions</title>
<para>
Toutes les expressions utilisées dans les instructions
<application>PL/pgSQL</application> sont traitées par l'exécuteur
<acronym>SQL</acronym> classique du serveur. En effet, une requête
comme
<synopsis>SELECT <replaceable>expression</replaceable>
</synopsis>
est traité par le moteur SQL principal. Bien qu'utilisant la commande
<command>SELECT</command>, tout nom de variable
<application>PL/pgSQL</application> est remplacé par des paramètres
(ceci est expliqué en détail dans la <xref linkend="plpgsql-var-subst"/>).
Cela permet au plan de requête du <command>SELECT</command> d'être
préparé une seule fois, puis d'être réutilisé pour les évaluations
suivantes avec différentes valeurs des variables. Du coup, ce qui arrive
réellement à la première utilisation d'une expression est simplement
une commande <command>PREPARE</command>. Par exemple, si nous déclarons
deux variables de type integer, <literal>x</literal> et
<literal>y</literal>, et que nous écrivons :
<programlisting>
IF x < y THEN ...
</programlisting>
ce qui se passe en arrière plan est ceci :
<programlisting>
PREPARE <replaceable>nom_instruction</replaceable>(integer, integer) AS SELECT $1 < $2;
</programlisting>
puis cette instruction préparée est exécutée (via <command>EXECUTE</command>)
pour chaque exécution de l'instruction <command>IF</command>, avec les
valeurs actuelles des variables <application>PL/pgSQL</application> fournies
en tant que valeurs des paramètres. Le plan de requête préparé de cette
façon est sauvegardé pour toute la durée de la connexion à la base, comme
le décrit la <xref linkend="plpgsql-plan-caching"/>. Généralement, ces détails
ne sont pas importants pour un utilisateur de
<application>PL/pgSQL</application>, mais ils sont utiles à connaître
pour diagnostiquer un problème.
</para>
</sect1>
<sect1 id="plpgsql-statements">
<title>Instructions de base</title>
<para>
Dans cette section ainsi que les suivantes, nous décrirons tous les types
d'instructions explicitement compris par
<application>PL/pgSQL</application>.
Tout ce qui n'est pas reconnu comme l'un de ces types d'instruction est présumé
être une commande SQL et est envoyé au moteur principal de bases de données pour
être exécutée comme décrit dans la <xref linkend="plpgsql-statements-sql-noresult"/>
et dans la <xref linkend="plpgsql-statements-sql-onerow"/>.
</para>
<sect2 id="plpgsql-statements-assignment">
<title>Affectation</title>
<para>
L'affectation d'une valeur à une variable <application>PL/pgSQL</application>
ou à un champ row/record s'écrit ainsi :
<synopsis><replaceable>variable</replaceable> := <replaceable>expression</replaceable>;
</synopsis>
Comme expliqué plus haut, l'expression dans cette instruction est évaluée au
moyen de la commande SQL <command>SELECT</command> envoyée au moteur principal de
bases de données. L'expression ne doit manier qu'une seule valeur.
</para>
<para>
Si le type de données du résultat de l'expression ne correspond pas au type de donnée
de la variable, ou que la variable a une taille ou une précision
(comme <type>char(20)</type>), la valeur résultat sera implicitement convertie
par l'interpréteur <application>PL/pgSQL</application> en utilisant la fonction
d'écriture (output-function) du type du résultat, et la fonction d'entrée
(input-function) du type de la variable. Notez que cela peut
conduire à des erreurs d'exécution générées par la fonction d'entrée si la forme
de la chaîne de la valeur résultat n'est pas acceptable pour cette fonction.
</para>
<para>
Exemples :
<programlisting>taxe := sous_total * 0.06;
mon_enregistrement.id_utilisateur := 20;
</programlisting>
</para>
</sect2>
<sect2 id="plpgsql-statements-sql-noresult">
<title>Exécuter une commande sans résultats</title>
<para>
Pour toute commande SQL qui ne renvoie pas de lignes, par exemple
<command>INSERT</command> sans clause <literal>RETURNING</literal>, vous
pouvez exécuter la commande à l'intérieur d'une fonction
<application>PL/pgSQL</application> rien qu'en écrivant la commande.
</para>
<para>
Tout nom de variable <application>PL/pgSQL</application> apparaissant
dans le texte de la commande est remplacé par un symbole de paramètre,
puis la valeur actuelle de la variable est fournie comme valeur du
paramètre à l'exécution. C'est le traitement exact décrit précédemment pour
les expressions. Pour les détails, voir la <xref
linkend="plpgsql-var-subst"/>. Par exemple, si vous écrivez
<programlisting>DECLARE
cle TEXT;
delta INTEGER;
BEGIN
...
UPDATE matable SET val = val + delta WHERE id = cle;
</programlisting>
le texte de la commande envoyée par le moteur SQL ressemble à ceci :
<programlisting> UPDATE matable SET val = val + $1 WHERE id = $2;
</programlisting>
Bien que vous n'ayez pas à y penser, il est utile de le savoir pour mieux
comprendre les messages d'erreur de syntaxe.
</para>
<caution>
<para>
<application>PL/pgSQL</application> substitutera à tout identifiant
une variable déclarée de la fonction. Du coup, une mauvaise idée serait
d'utiliser un nom de variable identique à celui d'une table, d'une
colonne ou d'une fonction que vous avez besoin d'utiliser dans des
commandes de la fonction. Pour plus d'informations, voir la <xref linkend="plpgsql-var-subst"/>.
</para>
</caution>
<para>
Lors de l'exécution d'une commande SQL de cette façon,
<application>PL/pgSQL</application> planifie la commande une fois et
ré-utilise ce plan lors des prochaines exécutions, pour la durée de vie
de la connexion. Les implications de ceci sont discutées en détail dans
la <xref linkend="plpgsql-plan-caching"/>.
</para>
<para>
Parfois, il est utile d'évaluer une expression ou une requête
<command>SELECT</command> mais sans récupérer le résultat, par
exemple lors de l'appel d'une fonction qui a des effets de bord
mais dont la valeur du résultat n'est pas utile. Pour faire cela
en <application>PL/pgSQL</application>, utilisez l'instruction
<command>PERFORM</command> :
<synopsis>PERFORM <replaceable>requête</replaceable>;
</synopsis>
Ceci exécute la <replaceable>requête</replaceable> et ne tient pas compte
du résultat. Écrivez la <replaceable>requête</replaceable> de la même
façon que vous écririez une commande <command>SELECT</command> mais
remplacez le mot clé initial <command>SELECT</command> avec
<command>PERFORM</command>.
Les variables <application>PL/pgSQL</application> seront substituées dans
la requête comme pour les commandes qui ne renvoient pas de résultat. Le
plan est mis en cache de la même façon. La variable spéciale
<literal>FOUND</literal> est configurée à true si la requête a produit
au moins une ligne, false dans le cas contraire (voir la
<xref linkend="plpgsql-statements-diagnostics"/>).
</para>
<note>
<para>
Vous pourriez vous attendre à ce que l'utilisation directe de
<command>SELECT</command> aboutisse au même résultat mais, actuellement,
la seule façon acceptée de le faire est d'utiliser
<command>PERFORM</command>. Une commande SQL qui peut renvoyer des lignes
comme <command>SELECT</command> sera rejetée comme une erreur si elle
n'a pas de clause <literal>INTO</literal>, ce qui est discuté dans la
section suivante.
</para>
</note>
<para>
Un exemple :
<programlisting>PERFORM creer_vuemat('cs_session_page_requests_mv', ma_requete);
</programlisting>
</para>
</sect2>
<sect2 id="plpgsql-statements-sql-onerow">
<title>Exécuter une requête avec une seule ligne de résultats</title>
<indexterm zone="plpgsql-statements-sql-onerow">
<primary>SELECT INTO</primary>
<secondary>en PL/pgSQL</secondary>
</indexterm>
<indexterm zone="plpgsql-statements-sql-onerow">
<primary>RETURNING INTO</primary>
<secondary>en PL/pgSQL</secondary>
</indexterm>
<para>
Le résultat d'une commande SQL ne ramenant qu'une seule ligne (mais avec une
ou plusieurs colonnes) peut être affecté à une variable de type record, row ou
à une liste de variables scalaires. Ceci se fait en écrivant la commande SQL
de base et en ajoutant une clause <literal>INTO</literal>. Par exemple,
<synopsis>SELECT <replaceable>expressions_select</replaceable> INTO <optional>STRICT</optional> <replaceable>cible</replaceable> FROM ...;
INSERT ... RETURNING <replaceable>expressions</replaceable> INTO <optional>STRICT</optional> <replaceable>cible</replaceable>;
UPDATE ... RETURNING <replaceable>expressions</replaceable> INTO <optional>STRICT</optional> <replaceable>cible</replaceable>;
DELETE ... RETURNING <replaceable>expressions</replaceable> INTO <optional>STRICT</optional> <replaceable>cible</replaceable>;
</synopsis>
où <replaceable>cible</replaceable> peut être une variable de type record,
row ou une liste de variables ou de champs record/row séparées par
des virgules. Les variables <application>PL/pgSQL</application> seront
substituées dans le reste de la requête, et le plan est mis en cache
comme décrit ci-dessus pour les commandes qui ne renvoient pas de lignes.
Ceci fonctionne pour <command>SELECT</command>,
<command>INSERT</command>/<command>UPDATE</command>/<command>DELETE</command>
avec <literal>RETURNING</literal>, et les commandes utilitaires qui renvoient
des résultats de type rowset (comme <command>EXPLAIN</command>).
Sauf pour la clause <literal>INTO</literal>, la commande SQL est identique
à celle qui aurait été écrite en dehors de <application>PL/pgSQL</application>.
</para>
<tip>
<para>
Notez que cette interprétation de <command>SELECT</command> avec <literal>INTO</literal>
est assez différente de la commande habituelle <command>SELECT INTO</command> où la
cible <literal>INTO</literal> est une table nouvellement créée. Si vous
voulez créer une table à partir du résultat d'un
<command>SELECT</command> à l'intérieur d'une fonction
<application>PL/pgSQL</application>, utilisez la syntaxe
<command>CREATE TABLE ... AS SELECT</command>.
</para>
</tip>
<para>
Si une ligne ou une liste de variables est utilisée comme cible, les
colonnes du résultat de la requête doivent correspondre exactement à
la structure de la cible (nombre de champs et types de données).
Dans le cas contraire, une erreur sera rapportée à l'exécution.
Quand une variable record est la cible, elle se configure
automatiquement avec le type row des colonnes du résultat de la
requête.
</para>
<para>
La clause <literal>INTO</literal> peut apparaître pratiquement partout
dans la commande SQL. Elle est écrite soit juste avant soit juste après
la liste d'<replaceable>expressions_select</replaceable> dans une commande
<command>SELECT</command>, ou à la fin de la commande pour d'autres types
de commande. Il est recommandé de suivre cette convention au cas où
l'analyseur <application>PL/pgSQL</application> devient plus strict dans
les versions futures.
</para>
<para>
Si <literal>STRICT</literal> n'est pas spécifié dans la clause
<literal>INTO</literal>, alors
<replaceable>cible</replaceable> sera configuré avec la première ligne
renvoyée par la requête ou à NULL si la requête n'a renvoyé aucune ligne.
(Notez que <quote>la première ligne</quote> n'est bien définie que
si vous avez utilisé <literal>ORDER BY</literal>.) Toute ligne résultat
après la première ligne est annulée. Vous pouvez vérifier la valeur de la
variable spéciale <literal>FOUND</literal> (voir la
<xref linkend="plpgsql-statements-diagnostics"/>) pour déterminer si une
ligne a été renvoyée :
<programlisting>SELECT * INTO monrec FROM emp WHERE nom = mon_nom;
IF NOT FOUND THEN
RAISE EXCEPTION 'employé % introuvable', mon_nom;
END IF;
</programlisting>
Si l'option <literal>STRICT</literal> est indiquée, la requête doit
renvoyer exactement une ligne. Dans le cas contraire, une erreur sera rapportée à
l'exécution, soit <literal>NO_DATA_FOUND</literal> (aucune ligne) soit
<literal>TOO_MANY_ROWS</literal> (plus d'une ligne). Vous pouvez utiliser
un bloc d'exception si vous souhaitez récupérer l'erreur, par exemple :
<programlisting>BEGIN
SELECT * INTO STRICT monrec FROM emp WHERE nom = mon_nom;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'employé % introuvable', mon_nom;
WHEN TOO_MANY_ROWS THEN
RAISE EXCEPTION 'employé % non unique', mon_nom;
END;
</programlisting>
Une exécution réussie de la commande avec <literal>STRICT</literal> renvoie
toujours true pour <literal>FOUND</literal>.
</para>
<para>
Pour <command>INSERT</command>/<command>UPDATE</command>/<command>DELETE</command>
avec <literal>RETURNING</literal>, <application>PL/pgSQL</application>
rapporte une erreur si plus d'une ligne est renvoyée, même quand
<literal>STRICT</literal> n'est pas spécifié. Ceci est dû au fait qu'il
n'y a pas d'option comme <literal>ORDER BY</literal> qui pourrait
déterminer la ligne à renvoyer.
</para>
<note>
<para>
L'option <literal>STRICT</literal> correspond au comportement du
<command>SELECT INTO</command> d'Oracle PL/SQL et des instructions
relatives.
</para>
</note>
<para>
Pour gérer les cas où vous avez besoin de traiter plusieurs lignes de
résultat à partir d'une requête SQL, voir la <xref linkend="plpgsql-records-iterating"/>.
</para>