forked from gleu/pgdocs_fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plperl.xml
931 lines (807 loc) · 34.9 KB
/
plperl.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
<?xml version="1.0" encoding="ISO-8859-15"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<chapter id="plperl">
<title>PL/Perl - Langage de procédures Perl</title>
<indexterm zone="plperl">
<primary>PL/Perl</primary>
</indexterm>
<indexterm zone="plperl">
<primary>Perl</primary>
</indexterm>
<para>
PL/Perl est un langage de procédures chargeable qui vous permet d'écrire des
fonctions <productname>PostgreSQL</productname> dans le <ulink
url="http://www.perl.com">langage de programmation Perl</ulink>.
</para>
<para>
L'avantage habituellement cité quant à l'utilisation de Perl est que cela
permet l'utilisation des nombreux opérateurs et fonctions de <quote>gestion
de chaînes</quote> disponibles grâce à Perl dans des procédures stockées.
L'analyse de chaînes complexes se trouve facilité par l'utilisation de Perl
et des fonctions et structures de contrôles fournies dans PL/pgSQL.
</para>
<para>
Pour installer PL/Perl dans une base de données spécifique, utilisez la
commande <literal>createlang plperl <replaceable>nom_base</replaceable></literal>.
</para>
<tip>
<para>
Si un langage est installé dans <literal>template1</literal>, toutes les
bases de données créées ultérieurement disposeront automatiquement de ce
langage.
</para>
</tip>
<note>
<para>
Les utilisateurs des paquetages sources doivent explicitement autoriser la
construction de PL/Perl pendant le processus d'installation (se référer à
la <xref linkend="install-short"/> pour plus d'informations). Les utilisateurs
des paquetages binaires peuvent trouver PL/Perl dans un sous-paquetage
séparé.
</para>
</note>
<sect1 id="plperl-funcs">
<title>Fonctions et arguments PL/Perl</title>
<para>
Pour créer une fonction dans le langage PL/Perl, utilisez la syntaxe
standard <xref linkend="sql-createfunction"
endterm="sql-createfunction-title"/> :
<programlisting>CREATE FUNCTION <replaceable>nom_fonction</replaceable> (<replaceable>types-arguments</replaceable>) RETURNS
<replaceable>type-retour</replaceable> AS $$
# Corps de la fonction PL/Perl
$$ LANGUAGE plperl;
</programlisting>
Le corps de la fonction est du code Perl normal. En fait, le code
supplémentaire PL/Perl l'emballe dans une sous-routine Perl. Une fonction
PL/Perl doit toujours renvoyer une valeur scalaire. Vous pouvez renvoyer
des structures plus complexes (tableaux, enregistrements et ensembles) en
renvoyant une référence comme indiqué ci-dessous. Ne renvoyez jamais une
liste.
</para>
<note>
<para>
L'utilisation de sous-routines nommées est dangereux en Perl, spécialement
si elles font références à des variables lexicales dans la partie
englobante. Comme une fonction PL/Perl est englobée dans une sous-routine,
toute sous-routine nommée que vous créez sera englobée. En général, il est
bien plus sûr de créer des sous-routines anonymes que vous appelerez via
un <quote>coderef</quote>. Voir la page man de <literal>perldiag</literal>
pour les détails.
</para>
</note>
<para>
La syntaxe de la commande <command>CREATE FUNCTION</command> requiert que le
corps de la fonction soit écrit comme une constante de type chaîne. Il est
habituellement plus agréable d'utiliser les guillemets dollar (voir la <xref
linkend="sql-syntax-dollar-quoting"/>) pour cette constante. Si vous
choisissez d'utiliser la syntaxe d'échappement des chaînes <literal>E''</literal>,
vous devez doubler les marques de guillemets simples (<literal>'</literal>)
et les antislashs (<literal>\</literal>) utilisés dans le corps de la
fonction (voir la <xref linkend="sql-syntax-strings"/>).
</para>
<para>
Les arguments et les résultats sont manipulés comme dans n'importe quel
routine Perl : les arguments sont passés au tableau
<varname>@_</varname> et une valeur de retour
est indiquée par <literal>return</literal> ou par la dernière expression
évaluée dans la fonction.
</para>
<para>
Par exemple, une fonction retournant le plus grand de deux entiers
peut être définie comme suit :
<programlisting>CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$
if ($_[0] > $_[1]) { return $_[0]; }
return $_[1];
$$ LANGUAGE plperl;
</programlisting>
</para>
<para>
Si une valeur NULL en SQL<indexterm><primary>valeur NULL</primary><secondary
sortas="PL/Perl">en PL/Perl</secondary></indexterm> est passée à une fonction,
cet argument apparaîtra comme <quote>undefined</quote> en Perl. La
fonction définie ci-dessus ne se comportera pas correctement avec des
arguments NULL (en fait, tout se passera comme s'ils avaient été des zéros).
Nous aurions pu ajouter <literal>STRICT</literal> à la définition de la fonction
pour forcer <productname>PostgreSQL</productname> à faire quelque chose de
plus raisonnable : si une valeur NULL est passée en argument, la
fonction ne sera pas du tout appelée mais retournera automatiquement un
résultat NULL. D'une autre façon, nous aurions pu vérifier dans le corps de
la fonction la présence d'arguments NULL. Par exemple, supposons que nous
voulions que <function>perl_max</function> avec un argument NULL et un autre
non NULL retourne une valeur non NULL plutôt qu'une valeur NULL, on aurait
écrit :
<programlisting>CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$
my ($x,$y) = @_;
if (! defined $x) {
if (! defined $y) { return undef; }
return $y;
}
if (! defined $y) { return $x; }
if ($x > $y) { return $x; }
return $y;
$$ LANGUAGE plperl;
</programlisting>
Comme le montre l'exemple ci-dessus, passer une valeur NULL en SQL
à une fonction en PL/Perl retourne une valeur non définie. Et ceci,
que la fonction soit déclarée stricte ou non.
</para>
<para>
Dans un argument de fonction, tout ce qui n'est pas une référence est une
chaîne qui est dans la représentation texte externe standard de
<productname>PostgreSQL</productname> pour ce type de données. Dans le cas
de types numériques ou texte, Perl fera ce qu'il faut et le programmeur
n'aura pas à s'en soucier. Néanmoins, dans d'autres cas, l'argument aura
besoin d'être converti dans une forme qui est plus utilisable que Perl. Par
exemple, voici comment convertir un argument du type <type>bytea</type> dans
des données binaires non échappées :
<programlisting> my $arg = shift;
$arg =~ s!\\(?:\\|(\d{3}))!$1 ? chr(oct($1)) : "\\"!ge;
</programlisting>
</para>
<para>
De façon similaire, les valeurs renvoyées à
<productname>PostgreSQL</productname> doivent être dans le format textuel.
Par exemple, voici comment échapper des données binaires pour une valeur de
retour de type <type>bytea</type> :
<programlisting> $retval =~ s!(\\|[^ -~])!sprintf("\\%03o",ord($1))!ge;
return $retval;
</programlisting>
</para>
<para>
Perl peut renvoyer des tableaux <productname>PostgreSQL</productname>
comme référence à des tableaux Perl. Voici un exemple :
<programlisting>CREATE OR REPLACE function renvoit_tableau()
RETURNS text[][] AS $$
return [['a"b','c,d'],['e\\f','g']];
$$ LANGUAGE plperl;
select renvoit_tableau();
</programlisting>
</para>
<para>
Les arguments de type composite sont passés à la fonction en tant que
références d'un tableau de découpage, les clés du tableau de découpage étant
les noms des attributs du type composé. Voici un exemple :
<programlisting>CREATE TABLE employe (
nom text,
basesalaire integer,
bonus integer
);
CREATE FUNCTION empcomp(employe) RETURNS integer AS $$
my ($emp) = @_;
return $emp->{basesalaire} + $emp->{bonus};
$$ LANGUAGE plperl;
SELECT nom, empcomp(employe.*) FROM employe;
</programlisting>
</para>
<para>
Une fonction PL/Perl peut renvoyer un résultat de type composite en utilisant
la même approche : renvoyer une référence à un hachage qui a les
attributs requis. Par exemple
<programlisting> CREATE TYPE testligneperl AS (f1 integer, f2 text, f3 text);
CREATE OR REPLACE FUNCTION perl_ligne() RETURNS test_ligne_perl AS $$
return {f2 => 'hello', f1 => 1, f3 => 'world'};
$$ LANGUAGE plperl;
SELECT * FROM perl_row();
</programlisting>
Toute colonne dans le type de données déclaré du résultat qui n'est pas
présente dans le hachage sera renvoyée NULL.
</para>
<para>
Les fonctions PL/Perl peuvent aussi renvoyer des ensembles de types
scalaires ou composites. Habituellement, vous voulez renvoyer une ligne à
la fois, à la fois pour améliorer le temps de démarrage et pour éviter
d'allonger la queue de l'ensemble des résultats en mémoire. Vous pouvez
faire ceci avec <function>return_next</function> comme indiqué ci-dessous.
Notez qu'après le dernier <function>return_next</function>, vous devez
placer soit <literal>return</literal> soit (encore mieux) <literal>return
undef</literal>.
<programlisting>CREATE OR REPLACE FUNCTION perl_set_int(int)
RETURNS SETOF INTEGER AS $$
foreach (0..$_[0]) {
return_next($_);
}
return undef;
$$ LANGUAGE plperl;
SELECT * FROM perl_set_int(5);
CREATE OR REPLACE FUNCTION perl_set()
RETURNS SETOF test_ligne_perl AS $$
return_next({ f1 => 1, f2 => 'Hello', f3 => 'World' });
return_next({ f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' });
return_next({ f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' });
return undef;
$$ LANGUAGE plperl;
</programlisting>
Pour les petits ensembles de résultats, vous pouvez renvoyer une référence
à un tableau contenant soit des scalaires, soit des références à des
tableaux soit des références à des hachages de types simples, de types
tableaux ou de types composites. Voici quelques exemples simples pour
renvoyer l'ensemble complet du résultant en tant que référence de
tableau :
<programlisting>CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$
return [0..$_[0]];
$$ LANGUAGE plperl;
SELECT * FROM perl_set_int(5);
CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testligneperl AS $$
return [
{ f1 => 1, f2 => 'Bonjour', f3 => 'Monde' },
{ f1 => 2, f2 => 'Bonjour', f3 => 'PostgreSQL' },
{ f1 => 3, f2 => 'Bonjour', f3 => 'PL/Perl' }
];
$$ LANGUAGE plperl;
SELECT * FROM perl_set();</programlisting>
</para>
<para>
Si vous souhaitez utiliser le pragma <literal>strict</literal> dans votre code,
la façon la plus simple de le faire est de configurer (<command>SET</command>)
<literal>plperl.use_strict</literal> à true. Ce paramètre affecte les
compilations suivantes de fonctions <application>PL/Perl</application>, mais
pas les fonctions déjà compilées dans la session en cours. Pour configurer
le paramètre avant que <application>PL/Perl</application> ne soit chargé, il est
nécessaire d'avoir ajouté <quote><literal>plperl</literal></quote> dans la liste <xref
linkend="guc-custom-variable-classes"/> de
<filename>postgresql.conf</filename>.
</para>
<para>
Une autre façon d'utiliser le pragma <literal>strict</literal> est de
placer :
<programlisting>use strict;
</programlisting>
dans le corps de la fonction. Mais ceci fonctionne uniquement dans les
fonctions <application>PL/PerlU</application> car <literal>use</literal> n'est pas une
opération de confiance. Dans les fonctions <application>PL/Perl</application>, vous
pouvez utiliser à la place :
<programlisting>BEGIN { strict->import(); }
</programlisting>
</para>
</sect1>
<sect1 id="plperl-database">
<title>Accès à la base de données depuis PL/Perl</title>
<para>
L'accès à la base de données à l'intérieur de vos fonctions écrites en Perl
peut se faire à partir de la fonction <function>spi_exec_query</function>
décrite ci-dessous ou à partir du module expérimental <ulink
url="http://www.cpan.org/modules/by-module/DBD/APILOS/">
<literal>DBD::PgSPI</literal></ulink>
(aussi disponible sur un miroir du <ulink
url="http://www.cpan.org/SITES.html">
<acronym>CPAN</acronym></ulink>).
Ce module rend accessible un descripteur de base de données conforme à
<acronym>DBI</acronym> nommé
<varname>$pg_dbh</varname> qui peut être utilisé pour exécuter des
requêtes en utilisant la syntaxe habituelle de
<acronym>DBI</acronym>.<indexterm><primary>DBI</primary></indexterm>
</para>
<para>
Actuellement, PL/Perl fournit les commandes Perl supplémentaires :
<variablelist>
<varlistentry>
<term><literal><function>spi_exec_query</function>(<replaceable>query</replaceable> [, <replaceable>max-rows</replaceable>])</literal></term>
<term><literal><function>spi_query</function>(<replaceable>command</replaceable>)</literal></term>
<term><literal><function>spi_fetchrow</function>(<replaceable>cursor</replaceable>)</literal></term>
<term><literal><function>spi_prepare</function>(<replaceable>command</replaceable>, <replaceable>argument types</replaceable>)</literal></term>
<term><literal><function>spi_exec_prepared</function>(<replaceable>plan</replaceable>)</literal></term>
<term><literal><function>spi_query_prepared</function>(<replaceable>plan</replaceable> [, <replaceable>attributes</replaceable>], <replaceable>arguments</replaceable>)</literal></term>
<term><literal><function>spi_cursor_close</function>(<replaceable>cursor</replaceable>)</literal></term>
<term><literal><function>spi_freeplan</function>(<replaceable>plan</replaceable>)</literal></term>
<listitem>
<indexterm>
<primary>spi_exec_query</primary>
<secondary>en PL/Perl</secondary>
</indexterm>
<para>
<literal>spi_exec_query</literal> exécute une commande SQL et renvoie
l'ensemble complet de la ligne comme une référence à un table de
références hachées. <emphasis>Vous ne devez utiliser cette commande
que lorsque vous savez que l'ensemble de résultat sera relativement
petit.</emphasis> Voici un exemple d'une requête (commande
<command>SELECT</command>) avec le nombre optionnel maximum de
lignes :
<programlisting>$rv = spi_exec_query('SELECT * FROM ma_table', 5);</programlisting>
Ceci entrevoit cinq lignes au maximum de la table
<literal>ma_table</literal>. Si <literal>ma_table</literal> a une
colonne <literal>ma_colonne</literal>, vous obtenez la valeur de la
ligne <literal>$i</literal> du résultat de cette façon :
<programlisting>$foo = $rv->{rows}[$i]->{ma_colonne};</programlisting>
Le nombre total des lignes renvoyées d'une requête
<command>SELECT</command> peut être accédé de cette façon :
<programlisting>$nrows = $rv->{processed}</programlisting>
</para>
<para>
Voici un exemple en utilisant un type de commande différent :
<programlisting>$query = "INSERT INTO ma_table VALUES (1, 'test')";
$rv = spi_exec_query($query);</programlisting>
Ensuite, vous pouvez accéder au statut de la commande (c'est-à-dire,
<literal>SPI_OK_INSERT</literal>) de cette façon :
<programlisting>$res = $rv->{status};</programlisting>
Pour obtenir le nombre de lignes affectées, exécutez :
<programlisting>$nrows = $rv->{processed};</programlisting>
</para>
<para>
Voici un exemple complet :
<programlisting>CREATE TABLE test (
i int,
v varchar
);
INSERT INTO test (i, v) VALUES (1, 'première ligne');
INSERT INTO test (i, v) VALUES (2, 'deuxième ligne');
INSERT INTO test (i, v) VALUES (3, 'troisième ligne');
INSERT INTO test (i, v) VALUES (4, 'immortel');
CREATE OR REPLACE FUNCTION test_munge() RETURNS SETOF test AS $$
my $rv = spi_exec_query('select i, v from test;');
my $status = $rv->{status};
my $nrows = $rv->{processed};
foreach my $rn (0 .. $nrows - 1) {
my $row = $rv->{rows}[$rn];
$row->{i} += 200 if defined($row->{i});
$row->{v} =~ tr/A-Za-z/a-zA-Z/ if (defined($row->{v}));
return_next($row);
}
return undef;
$$ LANGUAGE plperl;
SELECT * FROM test_munge();
</programlisting>
</para>
<para>
<literal>spi_query</literal> et <literal>spi_fetchrow</literal>
fonctionnent ensemble comme une paire d'ensembles de lignes pouvant être
assez importants ou pour les cas où vous souhaitez renvoyer les lignes dès
qu'elles arrivent. <literal>spi_fetchrow</literal> fonctionne
<emphasis>seulement</emphasis> avec <literal>spi_query</literal>. L'exemple
suivant illustre comment vous les utilisez ensemble :
<programlisting>CREATE TYPE foo_type AS (the_num INTEGER, the_text TEXT);
CREATE OR REPLACE FUNCTION lotsa_md5 (INTEGER) RETURNS SETOF foo_type AS $$
use Digest::MD5 qw(md5_hex);
my $file = '/usr/share/dict/words';
my $t = localtime;
elog(NOTICE, "opening file $file at $t" );
open my $fh, '<', $file # ooh, it's a file access!
or elog(ERROR, "cannot open $file for reading: $!");
my @words = <$fh>;
close $fh;
$t = localtime;
elog(NOTICE, "closed file $file at $t");
chomp(@words);
my $row;
my $sth = spi_query("SELECT * FROM generate_series(1,$_[0]) AS b(a)");
while (defined ($row = spi_fetchrow($sth))) {
return_next({
the_num => $row->{a},
the_text => md5_hex($words[rand @words])
});
}
return;
$$ LANGUAGE plperlu;
SELECT * from lotsa_md5(500);
</programlisting>
</para>
<para>
<literal>spi_prepare</literal>, <literal>spi_query_prepared</literal>,
<literal>spi_exec_prepared</literal> et <literal>spi_freeplan</literal>
implémentent la même fonctionnalité mais pour les requêtes préparées. Une
fois que le plan de requêtes est préparé par un appel à
<literal>spi_prepare</literal>, le plan peut être utilisé à la place de la
requête, soit dans <literal>spi_exec_prepared</literal>, où le résultat est
identique à celui renvoyé par <literal>spi_exec_query</literal>, soit
<literal>spi_query_prepared</literal> qui renvoie un curseur exactement comme
le fait <literal>spi_query</literal>, qui pourra être passé par la suite à
<literal>spi_fetchrow</literal>.
</para>
<para>
L'avantage des requêtes préparées est qu'il est possible d'utiliser un plan
préparé pour plus d'une exécution de requête. Une fois le plan inutile, il
pourra être libéré avec <literal>spi_freeplan</literal> :
</para>
<para>
<programlisting>
CREATE OR REPLACE FUNCTION init() RETURNS INTEGER AS $$
$_SHARED{my_plan} = spi_prepare( 'SELECT (now() + $1)::date AS now', 'INTERVAL');
$$ LANGUAGE plperl;
CREATE OR REPLACE FUNCTION add_time( INTERVAL ) RETURNS TEXT AS $$
return spi_exec_prepared(
$_SHARED{my_plan},
$_[0],
)->{rows}->[0]->{now};
$$ LANGUAGE plperl;
CREATE OR REPLACE FUNCTION done() RETURNS INTEGER AS $$
spi_freeplan( $_SHARED{my_plan});
undef $_SHARED{my_plan};
$$ LANGUAGE plperl;
SELECT init();
SELECT add_time('1 day'), add_time('2 days'), add_time('3 days');
SELECT done();
add_time | add_time | add_time
------------+------------+------------
2005-12-10 | 2005-12-11 | 2005-12-12
</programlisting>
</para>
<para>
Notez que les indices des paramètres dans <literal>spi_prepare</literal>
sont définis via $1, $2, $3, etc, pour éviter de déclarer des chaînes de
requête entre guillemets doubles, ce qui pourrait amener à des bogues
difficilement supprimables.
</para>
<para>
Habituellement, <function>spi_fetchrow</function> devra être répété jusqu'à
ce qu'il renvoie <literal>undef</literal>, indiquant qu'il n'y a plus de
lignes à lire. Le curseur est automatiquement libéré quand
<function>spi_fetchrow</function> renvoie <literal>undef</literal>. Si vous
ne souhaitez pas lire toutes les lignes, appelez à la place
<function>spi_cursor_close</function> pour libérer le curseur. Un échec ici
résultera en des pertes mémoire.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal><function>elog</function>(<replaceable>level</replaceable>,
<replaceable>msg</replaceable>)</literal></term>
<listitem>
<indexterm>
<primary>elog</primary>
<secondary>en PL/Perl</secondary>
</indexterm>
<para>
Produit un message de trace ou d'erreur. Les niveaux possibles sont
<literal>DEBUG</literal>, <literal>LOG</literal>, <literal>INFO</literal>,
<literal>NOTICE</literal>, <literal>WARNING</literal> et <literal>ERROR</literal>.
<literal>ERROR</literal> lève une condition d'erreur ; si elle n'est pas
récupérée par le code Perl l'entourant, l'erreur se propage à
l'extérieur de la requête appelante, causant l'annulation de la
transaction ou sous-transaction en cours. Ceci est en fait identique à
la commande <literal>die</literal> de Perl. Les autres niveaux génèrent
seulement des messages de niveaux de priorité différents. Le fait que
les messages d'un niveau de priorité particulier soient rapportés au
client, écrit dans les journaux du serveur, voire les deux, est contrôlé
par les variables de configuration <xref
linkend="guc-log-min-messages"/> et <xref
linkend="guc-client-min-messages"/>. Voir le <xref
linkend="runtime-config"/> pour plus d'informations.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</sect1>
<sect1 id="plperl-data">
<title>Valeurs des données dans PL/Perl</title>
<para>
Les valeurs de l'argument fournies au code d'une fonction PL/Perl sont
simplement les arguments en entrée convertis dans la forme textuelle
(comme si elles avaient été affichées par une instruction
<command>SELECT</command>). En revanche, une commande <literal>return</literal>
acceptera toute chaîne dont le format en entrée est acceptable pour le
type déclaré en retour. Donc, à l'intérieur de la fonction PL/Perl,
toutes les valeurs sont simplement des chaînes de texte.
</para>
</sect1>
<sect1 id="plperl-global">
<title>Valeurs globales dans PL/Perl</title>
<para>
Vous pouvez utiliser le hachage global <varname>%_SHARED</varname> pour
stocker les données, incluant les références de code, entre les appels de
fonction pour la durée de vie de la session en cours.
</para>
<para>
Voici un exemple simple pour des données partagées :
<programlisting>CREATE OR REPLACE FUNCTION set_var(name text, val text) RETURNS text AS $$
if ($_SHARED{$_[0]} = $_[1]) {
return 'ok';
} else {
return "Ne peux pas initialiser la variable partagée $_[0] à $_[1]";
}
$$ LANGUAGE plperl;
CREATE OR REPLACE FUNCTION get_var(name text) RETURNS text AS $$
return $_SHARED{$_[0]};
$$ LANGUAGE plperl;
SELECT set_var('sample', 'Bonjour, PL/Perl ! Comment va ?');
SELECT get_var('sample');</programlisting>
</para>
<para>
Voici un exemple légèrement plus compliqué utilisant une référence de
code :
<programlisting>CREATE OR REPLACE FUNCTION ma_fonction() RETURNS void AS $$
$_SHARED{myquote} = sub {
my $arg = shift;
$arg =~ s/(['\\])/\\$1/g;
return "'$arg'";
};
$$ LANGUAGE plperl;
SELECT ma_fonction(); /* initialise la fonction */
/* Initialise une fonction qui utilise la fonction quote */
CREATE OR REPLACE FUNCTION utilise_quote(TEXT) RETURNS text AS $$
my $text_to_quote = shift;
my $qfunc = $_SHARED{myquote};
return &$qfunc($text_to_quote);
$$ LANGUAGE plperl;</programlisting>
(Vous pouviez avoir remplacé le code ci-dessus avec la seule ligne
<literal>return $_SHARED{myquote}->($_[0]);</literal>
au prix d'une mauvaise lisibilité.)
</para>
</sect1>
<sect1 id="plperl-trusted">
<title>Niveaux de confiance de PL/Perl</title>
<indexterm zone="plperl-trusted">
<primary>trusted</primary>
<secondary>PL/Perl</secondary>
</indexterm>
<para>
Normalement, PL/Perl est installé en tant que langage de programmation de
<quote>confiance</quote>, de nom <literal>plperl</literal>. Durant cette installation,
certaines commandes Perl sont désactivées pour préserver la sécurité. En
général, les commandes qui interagissent avec l'environnement sont
restreintes. Cela inclut les commandes sur les descripteurs de fichiers,
<literal>require</literal> et <literal>use</literal> (pour les modules
externes). Il n'est pas possible d'accéder aux fonctions et variables
internes du processus du serveur de base de données ou d'obtenir un accès au
niveau du système d'exploitation avec les droits du processus serveur, tel
qu'une fonction C peut le faire. Ainsi, n'importe quel utilisateur sans
droits sur la base de données est autorisé à utiliser ce langage.
</para>
<para>
Voici l'exemple d'une fonction qui ne fonctionnera pas car les commandes système
ne sont pas autorisées pour des raisons de sécurité :
<programlisting>CREATE FUNCTION badfunc() RETURNS integer AS $$
my $tmpfile = "/tmp/badfile";
open my $fh, '>', $tmpfile
or elog(ERROR, qq{could not open the file "$tmpfile": $!});
print $fh "Testing writing to a file\n";
close $fh or elog(ERROR, qq{could not close the file "$tmpfile": $!});
return 1;
$$ LANGUAGE plperl;
</programlisting>
La création de cette fonction échouera car le validateur détectera
l'utilisation par cette fonction d'une opération interdite.
</para>
<para>
Il est parfois souhaitable d'écrire des fonctions Perl qui ne sont pas
restreintes. Par exemple, on peut souhaiter vouloir envoyer des courriers
électroniques. Pour supporter ce cas de figure, PL/Perl peut aussi être
installé comme un langage <quote>douteux</quote> (habituellement nommé
<application>PL/PerlU</application>
<indexterm><primary>PL/PerlU</primary></indexterm>).
Dans ce cas, la totalité du langage Perl est accessible. Si la commande
<command>createlang</command> est utilisée pour installer le langage, le nom
du langage <literal>plperlu</literal> sélectionnera la version douteuse de
PL/Perl.
</para>
<para>
Les auteurs des fonctions <application>PL/PerlU</application> doivent faire attention au
fait que celles-ci ne puissent être utilisées pour faire quelque chose de non
désiré car cela donnera la possibilité d'agir comme si l'on possédait les
privilèges d'administrateur de la base de données. Il est à noter que le
système de base de données ne permet qu'aux super-utilisateurs de créer des
fonctions dans un langage douteux.
</para>
<para>
Si la fonction ci-dessus a été créée par un super-utilisateur en utilisant
le langage <literal>plperlu</literal>, l'exécution de celle-ci réussira.
</para>
<note>
<para>
Pour des raisons de sécurité, pour stopper une faille des opérations
privilégiées à partir de <application>PL/PerlU</application> vers
<application>PL/Perl</application>, ces deux langages doivent être exécutés
dans des instances séparées de l'interpréteur Perl. Si votre installation
Perl a été compilé de façon approprié, ce n'est pas un problème. Néanmoins,
toutes les installations ne sont pas compilées avec les options requises.
Si <productname>PostgreSQL</productname> détecte que c'est le cas, alors
il n'exécutera pas le deuxième interpréteur mais lèvera une erreur à la
place. En conséquence, dans une telle installation, vous ne pouvez pas
utiliser à la fois <application>PL/PerlU</application> et
<application>PL/Perl</application> dans le même processus serveur. Le
remède à ceci est d'obtenir une installation Perl créée avec les options
appropriées, nommément soit <literal>usemultiplicity</literal> soit
<literal>usethreads</literal> et <literal>useithreads</literal>. Pour plus
de détails, voir la page man de <literal>perlembed</literal>.
</para>
</note>
</sect1>
<sect1 id="plperl-triggers">
<title>Déclencheurs PL/Perl</title>
<para>
PL/Perl peut être utilisé pour écrire des fonctions pour déclencheurs.
Dans une fonction déclencheur, la référence hachée
<varname>$_TD</varname> contient des informations sur l'événement du
déclencheur en cours. <varname>$_TD</varname> est une variable globale
qui obtient une valeur locale séparée à chaque appel du déclencheur.
Les champs de la référence de hachage
<varname>$_TD</varname> sont :
<variablelist>
<varlistentry>
<term><literal>$_TD->{new}{foo}</literal></term>
<listitem>
<para>
Valeur <literal>NEW</literal> de la colonne <literal>foo</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{old}{foo}</literal></term>
<listitem>
<para>
Valeur <literal>OLD</literal> de la colonne <literal>foo</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{name}</literal></term>
<listitem>
<para>
Nom du déclencheur appelé
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{event}</literal></term>
<listitem>
<para>
Événement du déclencheur : <literal>INSERT</literal>,
<literal>UPDATE</literal>, <literal>DELETE</literal> ou <literal>UNKNOWN</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{when}</literal></term>
<listitem>
<para>
Quand le déclencheur a été appelé :
<literal>BEFORE</literal> (avant), <literal>AFTER</literal>
(après) ou <literal>UNKNOWN</literal> (inconnu)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{level}</literal></term>
<listitem>
<para>
Le niveau du déclencheur : <literal>ROW</literal> (ligne),
<literal>STATEMENT</literal> (instruction) ou
<literal>UNKNOWN</literal> (inconnu)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{relid}</literal></term>
<listitem>
<para>
L'OID de la table sur lequel le déclencheur a été exécuté
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{table_name}</literal></term>
<listitem>
<para>
Nom de la table sur lequel le déclencheur a été exécuté
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{relname}</literal></term>
<listitem>
<para>
Nom de la table sur lequel le déclencheur a été exécuté. Elle est obsolète
et pourrait être supprimée dans une prochaine version. Utilisez
$_TD->{table_name} à la place.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{table_schema}</literal></term>
<listitem>
<para>
Nom du schéma sur lequel le déclencheur a été exécuté.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>$_TD->{argc}</literal></term>
<listitem>
<para>
Nombre d'arguments de la fonction déclencheur
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>@{$_TD->{args}}</literal></term>
<listitem>
<para>
Arguments de la fonction déclencheur. N'existe pas si
<literal>$_TD->{argc}</literal> vaut 0.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Les déclencheurs peuvent renvoyer un des éléments suivants :
<variablelist>
<varlistentry>
<term><literal>return;</literal></term>
<listitem>
<para>
Exécute l'instruction
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>"SKIP"</literal></term>
<listitem>
<para>
N'exécute pas l'instruction
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>"MODIFY"</literal></term>
<listitem>
<para>
Indique que la ligne <literal>NEW</literal> a été modifiée par la
fonction déclencheur
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Voici un exemple d'une fonction déclencheur illustrant certains points
ci-dessus :
<programlisting>CREATE TABLE test (
i int,
v varchar
);
CREATE OR REPLACE FUNCTION valid_id() RETURNS trigger AS $$
if (($_TD->{new}{i} >= 100) || ($_TD->{new}{i} <= 0)) {
return "SKIP"; # passe la commande INSERT/UPDATE
} elsif ($_TD->{new}{v} ne "immortal") {
$_TD->{new}{v} .= "(modified by trigger)";
return "MODIFY"; # modifie la ligne et exécute la commande INSERT/UPDATE
} else {
return; # exécute la commande INSERT/UPDATE
}
$$ LANGUAGE plperl;
CREATE TRIGGER test_valid_id_trig
BEFORE INSERT OR UPDATE ON test
FOR EACH ROW EXECUTE PROCEDURE valid_id();</programlisting>
</para>
</sect1>
<sect1 id="plperl-missing">
<title>Limitations et fonctionnalités absentes</title>
<para>
Les fonctionnalités suivantes ne sont actuellement pas implémentées dans
PL/Perl, mais peuvent faire l'objet de contributions généreuses de votre part.
<itemizedlist>
<listitem>
<para>
Les fonctions PL/Perl ne peuvent pas s'appeler entre elles (parce
qu'elles sont considérées comme des sous-routines anonymes au sein de
Perl).
</para>
</listitem>
<listitem>
<para>
SPI n'est pas complètement implémenté.
</para>
</listitem>
<listitem>
<para>
Si vous récupérez des ensembles de données très importants en utilisant
<literal>spi_exec_query</literal>, vous devez être conscient qu'ils
iront tous en mémoire. Vous pouvez l'éviter en utilisant
<literal>spi_query</literal>/<literal>spi_fetchrow</literal> comme
montré précédemment.
</para>
<para>
Un problème similaire survient si une fonction renvoyant un ensemble
passe un gros ensemble de lignes à PostgreSQL via
<literal>return</literal>. Vous pouvez l'éviter aussi en utilisant à la
place <literal>return_next</literal> pour chaque ligne renvoyée, comme
indiqué précédemment.
</para>
</listitem>
</itemizedlist>
</para>
</sect1>
</chapter>