forked from gleu/pgdocs_fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ecpg.xml
5100 lines (4744 loc) · 171 KB
/
ecpg.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="ecpg">
<title><application>ECPG</application> - <acronym>SQL</acronym> embarqué dans
du C</title>
<indexterm zone="ecpg"><primary>SQL embarqué</primary><secondary>dans du
C</secondary></indexterm>
<indexterm zone="ecpg"><primary>C</primary></indexterm>
<indexterm zone="ecpg"><primary>ECPG</primary></indexterm>
<para>
Ce chapitre décrit le paquetage <acronym>SQL</acronym> embarqué pour
<productname>PostgreSQL</productname>. Il a été écrit par
Linus Tolke (<email>linus@epact.se</email>) et Michael Meskes
(<email>meskes@postgresql.org</email>). Originellement, il a été écrit pour
fonctionner avec le langage <acronym>C</acronym>. Il fonctionne aussi avec le
<acronym>C++</acronym>, mais il ne reconnaît pas encore toutes les
constructions <acronym>C++</acronym>.
</para>
<para>
Cette documentation est assez incomplète. Mais du fait de la standardisation
de cette interface, des informations complémentaires sont
disponibles au travers de nombreuses ressources traitant du SQL.
</para>
<sect1 id="ecpg-concept">
<title>Concept</title>
<para>
Un programme <acronym>SQL</acronym> embarqué consiste en du code écrit dans
un langage de programmation ordinaire, le <acronym>C</acronym> dans le cas
présent, mélangé à des commandes SQL incluses dans des sections
spécialement marquées. Pour construire le programme, le code source est
d'abord passé au préprocesseur <acronym>SQL</acronym> embarqué qui le
convertit en un programme <acronym>C</acronym> ordinaire. Il peut alors être
traité par un compilateur <acronym>C</acronym>.
</para>
<para>
Le <acronym>SQL</acronym> embarqué a des avantages par rapport aux autres méthodes
de gestion de commandes <acronym>SQL</acronym> dans du code C. Premièrement,
il gère le passage laborieux des informations de et vers les variables du
programme <acronym>C</acronym>. Deuxièmement, le code SQL du programme est
vérifié syntaxiquement au moment de la construction. Troisièmement, le
<acronym>SQL</acronym> embarqué en C est spécifié dans le standard
<acronym>SQL</acronym> et supporté par de nombreux systèmes de bases de
données <acronym>SQL</acronym>. L'implantation <productname>PostgreSQL</productname>
est conçue pour correspondre au mieux à ce standard. Il est de ce fait
assez facile de porter les programmes <acronym>SQL</acronym>
embarqués écrits pour d'autres bases de données SQL vers
<productname>PostgreSQL</productname>.
</para>
<para>
Comme indiqué précédemment, les programmes écrits pour l'interface <acronym>SQL</acronym>
embarqué sont des programmes C normaux contenant un code spécial inséré pour
réaliser les actions en relation avec la base de données. Ce code spécial a
toujours la forme :
<programlisting>EXEC SQL ...;
</programlisting>
Ces instructions prennent syntaxiquement la place d'une instruction C.
Suivant l'instruction particulière, elles peuvent apparaître dans le
contexte global ou à l'intérieur d'une fonction. Les instructions
<acronym>SQL</acronym> embarquées suivent les règles de sensibilité à la
casse d'un code <acronym>SQL</acronym> normal, et non pas ceux du C.
</para>
<para>
Les sections suivantes expliquent toutes les instructions SQL embarquées.
</para>
</sect1>
<sect1 id="ecpg-connect">
<title>Se connecter au serveur de bases de données</title>
<para>
La connexion à une base de données se fait à l'aide de l'instruction
suivante :
<programlisting>EXEC SQL CONNECT TO <replaceable>cible</replaceable> <optional>AS <replaceable>nom-connexion</replaceable></optional> <optional>USER <replaceable>nom-utilisateur</replaceable></optional>;
</programlisting>
La <replaceable>cible</replaceable> peut être indiquée d'une des façons
suivantes :
<itemizedlist>
<listitem>
<simpara><literal><replaceable>nom_base</replaceable><optional>@<replaceable>nomhôte</replaceable>
</optional><optional>:<replaceable>port</replaceable></optional></literal>
</simpara>
</listitem>
<listitem>
<simpara><literal>tcp:postgresql://<replaceable>nomhôte</replaceable>
<optional>:<replaceable>port</replaceable> </optional>
<optional>/<replaceable>nom_base</replaceable></optional><optional>?<replaceable>
options</replaceable></optional></literal>
</simpara>
</listitem>
<listitem>
<simpara>
<literal>unix:postgresql://<replaceable>nomhôte</replaceable><optional>:
<replaceable>port</replaceable></optional><optional>/<replaceable>nom_base</replaceable>
</optional><optional>?<replaceable> options</replaceable></optional></literal>
</simpara>
</listitem>
<listitem>
<simpara>
une chaîne SQL contenant une des formes précédentes
</simpara>
</listitem>
<listitem>
<simpara>
une référence à une variable contenant une des formes précédentes (voir les
exemples)
</simpara>
</listitem>
<listitem>
<simpara>
<literal>DEFAULT</literal>
</simpara>
</listitem>
</itemizedlist>
Si la cible de connexion est indiquée littéralement (c'est-à-dire non pas via une
variable de référence) et que la valeur n'est pas mise entre guillemets,
les règles d'insensibilité à la casse du SQL standard sont appliquées.
Dans ce cas, il est possible, si cela s'avère nécessaire, d'encadrer
séparément les paramètres individuels de guillemets doubles.
En pratique, l'utilisation d'une chaîne littérale (entre guillemets
simples) ou d'une variable de référence engendre moins d'erreurs. La cible de
connexion <literal>DEFAULT</literal> initie une connexion à la base de
données standard avec l'utilisateur standard. Aucun nom d'utilisateur ou
de connexion ne peut être indiqué isolément dans ce cas.
</para>
<para>
Il existe également différentes façons de préciser le nom de l'utilisateur :
<itemizedlist>
<listitem>
<simpara>
<literal><replaceable>nomutilisateur</replaceable></literal>
</simpara>
</listitem>
<listitem>
<simpara>
<literal><replaceable>nomutilisateur</replaceable>/
<replaceable>motdepasse</replaceable></literal>
</simpara>
</listitem>
<listitem>
<simpara>
<literal><replaceable>nomutilisateur</replaceable> IDENTIFIED BY
<replaceable>motdepasse</replaceable></literal>
</simpara>
</listitem>
<listitem>
<simpara>
<literal><replaceable>nomutilisateur</replaceable> USING
<replaceable>motdepasse</replaceable></literal>
</simpara>
</listitem>
</itemizedlist>
Comme indiqué ci-dessus, les paramètres
<replaceable>nomutilisateur</replaceable> et
<replaceable>motdepasse</replaceable> peuvent être un identificateur SQL, une
chaîne SQL ou une référence à une variable de type caractère.
</para>
<para>
<replaceable>nom-connexion</replaceable> est utilisé pour gérer plusieurs
connexions dans un même programme. Il peut être omis si le programme n'utilise
qu'une seule connexion. La connexion la plus récemment ouverte devient la
connexion courante, utilisée par défaut lorsqu'une instruction SQL est à
exécuter (voir plus loin dans ce chapitre).
</para>
<para>
Quelques exemples d'instructions <command>CONNECT</command> :
<programlisting>EXEC SQL CONNECT TO ma_base@sql.mondomaine.com;
EXEC SQL CONNECT TO unix:postgresql://sql.mondomaine.com/ma_base AS maconnexion USER john;
EXEC SQL BEGIN DECLARE SECTION;
const char *cible = "ma_base@sql.mondomaine.com";
const char *utilisateur = "john";
EXEC SQL END DECLARE SECTION;
...
EXEC SQL CONNECT TO :cible USER :utilisateur;
</programlisting>
La dernière forme utilise la variante de référence de variable
caractère, citée plus haut. L'utilisation de variables C dans les
instructions SQL, en les préfixant d'un caractère deux-points, est expliqué
dans les prochaines sections.
</para>
<para>
Le format de la cible de connexion n'est pas spécifié dans
le standard SQL. Ainsi, lorsque l'on souhaite développer des applications portables,
il est préférable d'utiliser une syntaxe fondée sur le dernier exemple ci-dessus
pour encapsuler la chaîne de la cible de connexion.
</para>
</sect1>
<sect1 id="ecpg-disconnect">
<title>Fermer une connexion</title>
<para>
Pour fermer une connexion, l'instruction suivante est utilisée :
<programlisting>EXEC SQL DISCONNECT <optional><replaceable>connexion</replaceable></optional>;
</programlisting>
<replaceable>connexion</replaceable> peut être indiquée de
différentes façons :
<itemizedlist>
<listitem>
<simpara>
<literal><replaceable>nom-connexion</replaceable></literal>
</simpara>
</listitem>
<listitem>
<simpara>
<literal>DEFAULT</literal>
</simpara>
</listitem>
<listitem>
<simpara>
<literal>CURRENT</literal>
</simpara>
</listitem>
<listitem>
<simpara>
<literal>ALL</literal>
</simpara>
</listitem>
</itemizedlist>
Si aucun nom de connexion n'est indiqué, la connexion en cours est fermée.
</para>
<para>
Il est toujours préférable qu'une application ferme explicitement
chaque connexion qu'elle a ouverte.
</para>
</sect1>
<sect1 id="ecpg-commands">
<title>Exécuter des commandes SQL</title>
<para>
Toute commande SQL peut être exécutée à l'intérieur d'une application SQL
embarquée. Ci-dessous se trouvent quelques exemples de procédures.
</para>
<para>
Création d'une table :
<programlisting>EXEC SQL CREATE TABLE foo (nombre integer, ascii char(16));
EXEC SQL CREATE UNIQUE INDEX num1 ON foo(nombre);
EXEC SQL COMMIT;
</programlisting>
</para>
<para>
Insertion de lignes :
<programlisting>EXEC SQL INSERT INTO foo (nombre, ascii) VALUES (9999, 'doodad');
EXEC SQL COMMIT;
</programlisting>
</para>
<para>
Suppression de lignes :
<programlisting>EXEC SQL DELETE FROM foo WHERE nombre = 9999;
EXEC SQL COMMIT;
</programlisting>
</para>
<para>
Sélection d'une ligne :
<programlisting>EXEC SQL SELECT foo INTO :FooBar FROM table1 WHERE ascii = 'doodad';
</programlisting>
</para>
<para>
Sélection à l'aide de curseurs :
<programlisting>EXEC SQL DECLARE foo_bar CURSOR FOR
SELECT nombre, ascii FROM foo
ORDER BY ascii;
EXEC SQL OPEN foo_bar;
EXEC SQL FETCH foo_bar INTO :FooBar, DooDad;
...
EXEC SQL CLOSE foo_bar;
EXEC SQL COMMIT;
</programlisting>
</para>
<para>
Actualisations :
<programlisting>EXEC SQL UPDATE foo
SET ascii = 'foobar'
WHERE nombre = 9999;
EXEC SQL COMMIT;
</programlisting>
</para>
<para>
Les lexèmes de la forme
<quote><literal>:<replaceable>quelquechose</replaceable></literal></quote>
sont des <firstterm>variables hôtes</firstterm>, c'est-à-dire qu'elles font
référence à des variables du programme C. Elles sont expliquées dans
la <xref linkend="ecpg-variables"/>.
</para>
<para>
Dans le mode par défaut, les instructions ne sont validées que lorsque
<command>EXEC SQL COMMIT</command> est exécuté. L'interface SQL embarqué
supporte aussi la validation automatique des transactions (similaire au
comportement de <application>libpq</application>) via l'option
<option>-t</option> en ligne de commande pour
<command>ecpg</command> (voir ci-dessous) ou via l'instruction <literal>EXEC
SQL SET AUTOCOMMIT TO ON</literal>. En mode de validation automatique, chaque
commande est automatiquement validée sauf si elle est à l'intérieur d'un bloc
de transaction explicite. Ce mode peut être explicitement désactivé en
utilisant <literal>EXEC SQL SET AUTOCOMMIT TO OFF</literal>.
</para>
</sect1>
<sect1 id="ecpg-set-connection">
<title>Choisir une connexion</title>
<para>
Les instructions SQL affichées dans la section précédente sont exécutées à
partir de la connexion courante, c'est-à-dire la dernière à avoir été ouverte.
Il y a deux façons de gérer l'utilisation de plusieurs connexions dans une
application.
</para>
<para>
La première option est de choisir explicitement une connexion pour chaque
instruction SQL, par exemple :
<programlisting>EXEC SQL AT <replaceable>nom-connexion</replaceable> SELECT ...;
</programlisting>
Cette option est particulièrement adaptée si l'application a besoin
d'utiliser alternativement plusieurs connexions.
</para>
<para>
Si l'application utilise plusieurs threads (fil) d'exécution, ils ne peuvent
pas concurrement partager une connexion. Il faut, soit contrôler explicitement l'accès
à la connexion (en utilisant des mutex), soit utiliser une connexion pour
chaque thread. Si chaque thread utilise sa propre connexion, il est
nécessaire d'utiliser la clause AT pour préciser la connexion utilisée par le
thread.
</para>
<para>
La seconde option consiste à exécuter une instruction pour basculer la connexion
courante. L'instruction est :
<programlisting>EXEC SQL SET CONNECTION <replaceable>nom-connexion</replaceable>;
</programlisting>
Cette option est particulièrement intéressante si un grand nombre
d'instructions doivent être exécutées à partir de la même connexion. Elle ne
gère pas les threads.
</para>
</sect1>
<sect1 id="ecpg-variables">
<title>Utiliser des variables hôtes</title>
<para>
La <xref linkend="ecpg-commands"/> présente l'exécution d'instructions
SQL à partir d'un programme SQL embarqué. Certaines de ces
instructions n'utilisent que des valeurs fixes. Elles n'offrent pas la
possibilité d'insérer des valeurs fournies par l'utilisateur dans les
instructions. Elles ne permettent pas non plus au programme de traiter
les valeurs renvoyées par la requête.
Ces types d'instructions ne sont pas vraiment utiles dans les
applications réelles. Cette section explique en détails
l'échange de données entre le programme C et les instructions SQL embarquées
à l'aide d'un mécanisme simple appelé <firstterm>variables
hôtes</firstterm>. Dans un programme SQL embarqué, les
instructions SQL sont considérées comme <firstterm>invitées</firstterm> dans le code du
programme C qui est le <firstterm>langage hôte</firstterm>. Du coup, les
variables du programme C sont appelées <firstterm>variables hôtes</firstterm>.
</para>
<sect2>
<title>Aperçu</title>
<para>
L'échange de données entre le programme C et les instructions SQL est
particulièrement simple en SQL embarqué. Plutôt que de laisser le programme
copier les données dans l'instruction, ce qui implique un certain nombre de
complications, dont la bonne mise entre guillemets de la valeur, il est plus simple
d'écrire le nom de la variable C dans l'instruction SQL en la préfixant par un
caractère deux-points. Par exemple :
<programlisting>EXEC SQL INSERT INTO unetable VALUES (:v1, 'foo', :v2);
</programlisting>
Cette instruction fait référence à deux variables C nommées
<varname>v1</varname> et <varname>v2</varname>, et utilise également une
chaîne SQL pour illustrer l'absence de restriction à l'utilisation
d'un type de données ou d'un autre.
</para>
<para>
Ce style d'insertion de variables C dans des instructions SQL fonctionne
dans tous les cas où l'on attend une expression de valeur dans une instruction SQL.
</para>
</sect2>
<sect2>
<title>Sections de déclaration</title>
<para>
Pour passer des données du programme à la base de données,
comme paramètres d'une requête par exemple, ou pour passer des données de la
base au programme, les variables C supposées contenir ces données doivent être
déclarées dans des sections spécialement marquées pour que le préprocesseur
du SQL embarqué soit averti de leur présence.
</para>
<para>
Cette section commence avec :
<programlisting>EXEC SQL BEGIN DECLARE SECTION;
</programlisting>
et se termine avec :
<programlisting>EXEC SQL END DECLARE SECTION;
</programlisting>
Entre ces lignes, on trouve des déclarations normales de variables C,
comme :
<programlisting>int x = 4;
char foo[16], bar[16];
</programlisting>
Une valeur initiale optionnelle peut être affectée à la variable.
La portée de la variable est déterminée par son emplacement
dans la section de déclaration du programme.
Les variables peuvent aussi être déclarées avec la syntaxe suivante qui crée
implicitement une section de déclaration :
<programlisting>EXEC SQL int i = 4;
</programlisting>
Le nombre de sections de déclarations dans un programme n'est pas limité.
</para>
<para>
Les déclarations sont aussi placées dans le fichier de sortie comme des
variables C normales. Du coup, il n'est plus nécessaire de les déclarer à
nouveau. Les variables qui n'ont pas pour but d'être utilisées dans des
commandes SQL peuvent être normalement déclarées en dehors des sections
spéciales.
</para>
<para>
La définition d'une structure ou union doit aussi être saisie dans une
section <literal>DECLARE</literal>. Sinon, le préprocesseur, ne connaissant pas leur
définition, ne peut pas gérer ces types.
</para>
</sect2>
<!-- variables hôtes ou variables hôte ? -->
<sect2>
<title>Les différents types de variables hôtes</title>
<para>
Des tableaux (<foreignphrase>array</foreignphrase>), définitions de type
(<foreignphrase>typedef</foreignphrase>), structures
(<foreignphrase>struct</foreignphrase>) et pointeurs
(<foreignphrase>pointer</foreignphrase>) peuvent aussi être utilisés comme
variables hôtes. Il existe également des types spéciaux de
variables hôtes qui n'existent qu'en ECPG.
</para>
<para>
Quelques exemples sur les variables hôtes :
<variablelist>
<varlistentry>
<term>Tableaux</term>
<listitem>
<para>
Une des utilisations les plus communes d'une déclaration de tableaux
est certainement l'allocation d'un tableau de caractères :
<programlisting>EXEC SQL BEGIN DECLARE SECTION;
char chaine[50];
EXEC SQL END DECLARE SECTION;
</programlisting>
C'est à l'utilisateur de gérer la longueur du tableau. Si une telle
variable hôte est utilisée comme variable cible d'une requête qui
renvoie une chaîne de plus de 49 caractères, un dépassement de tampon
survient.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Typedefs</term>
<listitem>
<para>
Le mot clé <literal>typedef</literal> est utilisé pour faire correspondre
de nouveaux types à des types déjà existants.
<programlisting>EXEC SQL BEGIN DECLARE SECTION;
typedef char montypecaractere[40];
typedef long serial_t;
EXEC SQL END DECLARE SECTION;
</programlisting>
On peut aussi utiliser :
<programlisting>EXEC SQL TYPE serial_t IS long;
</programlisting>
Cette déclaration n'a pas besoin de faire partie d'une section de
déclaration.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Pointeurs</term>
<listitem>
<para>
Des pointeurs peuvent être déclarés vers les types les plus communs.
Néanmoins, Les pointeurs en
tant que variables cibles de requêtes ne peuvent être utilisés sans
allocation automatique.
Voir <xref linkend="ecpg-descriptors"/> pour plus d'informations sur
l'allocation automatique.
</para>
<programlisting>EXEC SQL BEGIN DECLARE SECTION;
int *intp;
char **charp;
EXEC SQL END DECLARE SECTION;
</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term>Types spéciaux de variables</term>
<listitem>
<para>
ECPG contient certains types spéciaux qui facilitent l'interaction
avec les données du serveur SQL. Par exemple, le support
des types <type>varchar</type>, <type>numeric</type>, <type>date</type>,
<type>timestamp</type> et <type>interval</type> a été
implanté.
<xref linkend="ecpg-pgtypes"/> contient des fonctions basiques de
gestion de ces types. Il n'est ainsi pas nécessaire d'envoyer
une requête au serveur SQL simplement pour ajouter un interval
à une variable de type timestamp, par exemple.
</para>
<para>
Le type spécial <type>VARCHAR</type> est converti en une
<type>struct</type> nommée pour chaque variable. La déclaration :
<programlisting>VARCHAR var[180];
</programlisting>
est convertie en :
<programlisting>struct varchar_var { int len; char arr[180]; } var;
</programlisting>
Cette structure convient pour interfacer des données SQL
de type <type>varchar</type>.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</sect2>
<sect2>
<title><command>SELECT INTO</command> et <command>FETCH
INTO</command></title>
<para>
Les sections précédentes expliquent le passage de données entre
l'application et une commande SQL. Pour récupérer le résultat d'une
requête, le SQL embarqué fournit des variantes spéciales
des commandes habituelles <command>SELECT</command> et
<command>FETCH</command>. Ces commandes ont une clause
<literal>INTO</literal> particulière qui indique les variables hôtes de
stockage des valeurs récupérées.
</para>
<para>
Par exemple :
<programlisting>/*
* Soit la table :
* CREATE TABLE test1 (a int, b varchar(50));
*/
EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;
...
EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;
</programlisting>
La clause <literal>INTO</literal> apparaît donc entre la liste de
sélection et la clause <literal>FROM</literal>.
Le nombre d'éléments dans la liste du <command>select</command>
et dans la liste qui suit <literal>INTO</literal> (aussi appelée liste
cible) doivent être identiques.
</para>
<para>
Exemple utilisant la commande <command>FETCH</command> :
<programlisting>EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;
...
EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test;
...
do {
...
EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2;
...
} while (...);
</programlisting>
Ici, la clause <literal>INTO</literal> apparaît après toutes les autres
clauses.
</para>
<para>
Ces deux méthodes ne permettent de récupérer qu'une ligne à la
fois. Pour traiter des ensembles de résultats
contenant potentiellement plus d'une ligne, il faut utiliser un
curseur, comme indiqué dans le second exemple.
</para>
</sect2>
<sect2>
<title>Indicateurs</title>
<para>
Les exemples ci-dessus ne gèrent pas les valeurs NULL. En fait, ces
exemples de récupération affichent une erreur s'ils récupèrent une
valeur NULL à partir de la base de données. Pour être capable de passer des
valeurs NULL à la base de données ou de récupérer des valeurs NULL de la
base de données, il est nécessaire d'ajouter une deuxième spécification de
variable hôte pour chaque variable hôte contenant des données. Cette seconde
variable est appelée l'<firstterm>indicateur</firstterm> et contient un
drapeau indiquant si la valeur est NULL, auquel cas la valeur de la variable
hôte réelle est ignorée. Exemple qui gère correctement la
récupération de valeurs NULL :
<programlisting>EXEC SQL BEGIN DECLARE SECTION;
VARCHAR val;
int val_ind;
EXEC SQL END DECLARE SECTION:
...
EXEC SQL SELECT b INTO :val :val_ind FROM test1;
</programlisting>
La variable indicateur <varname>val_ind</varname> vaut zéro si la valeur
n'est pas nulle, elle est négative dans le cas contraire.
</para>
<para>
L'indicateur a une autre fonction : si la valeur de l'indicateur est
positive, cela signifie que la valeur n'est pas nulle mais qu'elle a été
tronquée lors de son stockage dans la variable hôte.
</para>
</sect2>
</sect1>
<sect1 id="ecpg-dynamic">
<title>SQL dynamique</title>
<para>
Dans de nombreux cas, les instructions SQL particulières qu'une application
doit exécuter sont connues au moment de l'écriture de l'application.
Néanmoins, dans certains cas, les instructions SQL sont composées à
l'exécution ou fournies par une source externe. Dans ce cas, il n'est pas possible
d'embarquer directement les instructions SQL dans le code source C, mais il
existe une fonctionnalité permettant d'appeler des instructions SQL arbitraires
fournies par l'intermédiaire d'une variable de type chaîne.
</para>
<para>
La façon la plus simple d'exécuter une instruction SQL arbitraire est
d'utiliser la commande <command>EXECUTE IMMEDIATE</command>. Par
exemple :
<programlisting>EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "CREATE TABLE test1 (...);";
EXEC SQL END DECLARE SECTION;
EXEC SQL EXECUTE IMMEDIATE :stmt;
</programlisting>
Les instructions de ce type ne peuvent pas être utilisées pour récupérer des
données (c'est-à-dire un <command>SELECT</command>).
</para>
<para>
Une façon plus puissante d'exécuter des instructions SQL arbitraires est de
les préparer une seule fois et de les exécuter ensuite aussi souvent que nécessaire.
Il est
également possible de préparer une version généralisée d'une instruction, puis
d'exécuter les versions spécifiques en substituant les paramètres. Lors de la
préparation de l'instruction, il suffit d'écrire des points d'interrogation
aux endroits où des paramètres seront substitués par la suite. Par exemple :
<programlisting>EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "INSERT INTO test1 VALUES(?, ?);";
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
...
EXEC SQL EXECUTE mystmt USING 42, 'foobar';
</programlisting>
Si l'instruction exécutée retourne des valeurs, il est nécessaire d'ajouter une
clause <literal>INTO</literal> :
<programlisting><![CDATA[EXEC SQL BEGIN DECLARE SECTION;
const char *stmt = "SELECT a, b, c FROM test1 WHERE a > ?";
int v1, v2;
VARCHAR v3;
EXEC SQL END DECLARE SECTION;
EXEC SQL PREPARE mystmt FROM :stmt;
...
EXEC SQL EXECUTE mystmt INTO v1, v2, v3 USING 37;
]]></programlisting>
Une commande <command>EXECUTE</command> peut avoir une clause
<literal>INTO</literal>, une clause <literal>USING</literal>, les deux ou
aucune.
</para>
<para>
Lorsqu'une instruction préparée n'est plus utile, il est préférable de la
désallouer :
<programlisting>EXEC SQL DEALLOCATE PREPARE <replaceable>name</replaceable>;
</programlisting>
</para>
</sect1>
<sect1 id="ecpg-pgtypes">
<title>Bibliothèque pgtypes</title>
<para>
La bibliothèque pgtypes établit une correspondance entre les types
<productname>PostgreSQL</productname> et les équivalents en C. Elle fournit
aussi des fonctions permettant des calculs simples sur ces types en C,
c'est-à-dire sans l'aide du serveur <productname>PostgreSQL</productname>.
Par exemple :
<programlisting><![CDATA[EXEC SQL BEGIN DECLARE SECTION;
date date1;
timestamp ts1, tsout;
interval iv1;
char *out;
EXEC SQL END DECLARE SECTION;
PGTYPESdate_today(&date1);
EXEC SQL SELECT started, duration INTO :ts1, :iv1 FROM datetbl WHERE d=:date1;
PGTYPEStimestamp_add_interval(&ts1, &iv1, &tsout);
out = PGTYPEStimestamp_to_asc(&tsout);
printf("Started + duration: %s\n", out);
free(out);
]]></programlisting>
</para>
<sect2>
<title>Le type numeric</title>
<para>
Le type numeric permet des calculs de précision arbitraire. Voir
<xref linkend="datatype-numeric"/> pour le type équivalent dans le serveur
<productname>PostgreSQL</productname>. Du fait de la précision arbitraire,
une variable de ce type doit pouvoir étendre et diminuer sa taille dynamiquement.
C'est pourquoi il est obligatoire d'utiliser les fonctions
<function>PGTYPESnumeric_new</function> et
<function>PGTYPESnumeric_free</function> pour créer de telles variables,
et uniquement en mémoire <foreignphrase>heap</foreignphrase>.
Le type decimal, similaire mais limité en précision, peut être créé sur la
pile comme sur le <quote>heap</quote>.
</para>
<para>
Les fonctions suivantes peuvent être utilisées avec le type numeric :
<variablelist>
<varlistentry>
<term><function>PGTYPESnumeric_new</function></term>
<listitem>
<para>
réclame un pointeur vers une variable de type numeric nouvellement allouée.
<synopsis>numeric *PGTYPESnumeric_new(void);
</synopsis>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_free</function></term>
<listitem>
<para>
libère un type numeric en vidant toute sa mémoire.
<synopsis>void PGTYPESnumeric_free(numeric *var);
</synopsis>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_from_asc</function></term>
<listitem>
<para>
analyse un type numeric à partir de sa notation en chaîne.
<synopsis>numeric *PGTYPESnumeric_from_asc(char *str, char **endptr);
</synopsis>
Les formats valides sont, par exemple :
<literal>-2</literal>,
<literal>.794</literal>,
<literal>+3.44</literal>,
<literal>592.49E07</literal> ou
<literal>-32.84e-4</literal>.
Si la valeur a pu être analysée correctement, un pointeur valide est
renvoyé. Dans le cas contraire, il s'agit d'un pointeur NULL.
Actuellement, ecpg analyse toujours la chaîne complète et, du coup,
ne supporte pas le stockage de l'adresse du premier caractère
invalide dans <literal>*endptr</literal>.
<literal>endptr</literal> peut être initialisé à NULL en toute sécurité.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_to_asc</function></term>
<listitem>
<para>
renvoie un pointeur vers une chaîne allouée via <function>malloc</function>
qui contient la représentation en chaîne du type numeric <literal>num</literal>.
<synopsis>char *PGTYPESnumeric_to_asc(numeric *num, int dscale);
</synopsis>
La valeur numerique est affichée avec <literal>dscale</literal> chiffres
décimaux, arrondie si nécessaire.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_add</function></term>
<listitem>
<para>
additionne deux variables de type numeric dans une troisième.
<synopsis>int PGTYPESnumeric_add(numeric *var1, numeric *var2, numeric *result);
</synopsis>
La fonction additionne les variables <literal>var1</literal> et
<literal>var2</literal> dans la variable résultat
<literal>result</literal>.
Elle renvoie 0 en cas de succès et -1 en cas d'erreur.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_sub</function></term>
<listitem>
<para>
soustrait deux variables de type numeric et renvoie le résultat dans une
troisième.
<synopsis>int PGTYPESnumeric_sub(numeric *var1, numeric *var2, numeric *result);
</synopsis>
La fonction soustrait la variable <literal>var2</literal> à la variable
<literal>var1</literal>. Le résultat de cette opération est stocké dans la
variable <literal>result</literal>.
La fonction renvoie 0 en cas de succès et -1 en cas d'erreur.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_mul</function></term>
<listitem>
<para>
multiplie deux variables de type numeric et renvoie le résultat dans une
troisième.
<synopsis>
int PGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result);
</synopsis>
La fonction multiplie les variables <literal>var1</literal> et
<literal>var2</literal>. Le résultat de cette opération est stocké dans la
variable <literal>result</literal>.
La fonction renvoie 0 en cas de succès et -1 en cas d'erreur.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_div</function></term>
<listitem>
<para>
divise deux variables de type numeric et renvoie le résultat dans une
troisième.
<synopsis>
int PGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result);
</synopsis>
La fonction divise la variable <literal>var1</literal> par
<literal>var2</literal>. Le résultat de cette opération est stocké dans la
variable <literal>result</literal>.
La fonction renvoie 0 en cas de succès et -1 en cas d'erreur.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_cmp</function></term>
<listitem>
<para>
compare deux variables de type numeric.
<synopsis>
int PGTYPESnumeric_cmp(numeric *var1, numeric *var2)
</synopsis>
La fonction compare deux variables de type numeric. En cas
d'erreur, <literal>INT_MAX</literal> est renvoyé. En cas de succès, la
fonction renvoie un des trois résultats possibles :
<itemizedlist>
<listitem>
<para>
1, si <literal>var1</literal> est plus grand que <literal>var2</literal>
</para>
</listitem>
<listitem>
<para>
-1, si <literal>var1</literal> est plus petit que <literal>var2</literal>
</para>
</listitem>
<listitem>
<para>
0, si <literal>var1</literal> est égal à <literal>var2</literal>
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_from_int</function></term>
<listitem>
<para>
convertit une variable de type int en une variable de type numeric.
<synopsis>
int PGTYPESnumeric_from_int(signed int int_val, numeric *var);
</synopsis>
La fonction accepte une variable de type signed int (entier signé) et la stocke
dans la variable <literal>var</literal> de type numeric.
La fonction renvoie 0 en cas de succès et -1 en cas d'erreur.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_from_long</function></term>
<listitem>
<para>
convertit une variable de type long int en une variable de type numeric.
<synopsis>
int PGTYPESnumeric_from_long(signed long int long_val, numeric *var);
</synopsis>
La fonction accepte une variable de type signed long int (entier long
signé) et la stocke
dans la variable <literal>var</literal> de type numeric.
La fonction renvoie 0 en cas de succès et -1 en cas d'erreur.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>PGTYPESnumeric_copy</function></term>
<listitem>
<para>
copie une variable de type numeric dans une autre.
<synopsis>
int PGTYPESnumeric_copy(numeric *src, numeric *dst);
</synopsis>
la fonction copie la valeur de la variable sur laquelle pointe
<literal>src</literal> dans la variable sur laquelle pointe
<literal>dst</literal>.
La fonction renvoie 0 en cas de succès et -1 en cas d'erreur.
</para>
</listitem>
</varlistentry>
<!-- SAS : 20070522
Il y a le type numeric, type SQL, et le numérique qui est une
représentation sous la forme de chiffres d'un nombre -->
<varlistentry>
<term><function>PGTYPESnumeric_from_double</function></term>
<listitem>
<para>