forked from gleu/pgdocs_fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xtypes.xml
311 lines (270 loc) · 11 KB
/
xtypes.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
<?xml version="1.0" encoding="UTF-8"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<sect1 id="xtypes">
<title>Types utilisateur</title>
<indexterm zone="xtypes">
<primary>type de données</primary>
<secondary>utilisateur</secondary>
</indexterm>
<para>
Comme cela est décrit dans la <xref linkend="extend-type-system"/>,
<productname>PostgreSQL</productname> peut être étendu pour supporter de
nouveaux types de données. Cette section décrit la définition de nouveaux
types basiques. Ces types de données sont définis en-dessous du
<acronym>SQL</acronym>. Créer un nouveau type requiert d'implanter des
fonctions dans un langage de bas niveau, généralement
le C.
</para>
<para>
Les exemples de cette section sont disponibles dans
<filename>complex.sql</filename> et <filename>complex.c</filename>
du répertoire <filename>src/tutorial</filename> de la distribution.
Voir le fichier <filename>README</filename> de ce répertoire pour les instructions
d'exécution des exemples.
</para>
<para>
<indexterm>
<primary>fonction d'entrée</primary>
</indexterm>
<indexterm>
<primary>fonction de sortie</primary>
</indexterm>
Un type utilisateur doit toujours posséder des fonctions d'entrée et de sortie.
<indexterm>
<primary>fonction d'entrée</primary>
<secondary>d'un type de données</secondary>
</indexterm>
<indexterm>
<primary>fonction de sortie</primary>
<secondary>d'un type de données</secondary>
</indexterm>
Ces fonctions déterminent la présentation du type en chaînes de caractères
(pour la saisie par l'utilisateur et le renvoi à l'utilisateur) et son
organisation en mémoire. La fonction d'entrée prend comme argument une
chaîne de caractères terminée par NULL et retourne la représentation interne
(en mémoire) du type. La fonction de sortie prend en argument la
représentation interne du type et retourne une chaîne de caractères terminée
par NULL.
</para>
<para>
Il est possible de faire plus que stocker un type, mais il faut pour cela
implanter des fonctions supplémentaires gérant les opérations souhaitées.
</para>
<para>
Soit le cas d'un type <type>complex</type> représentant les nombres complexes. Une
façon naturelle de représenter un nombre complexe en mémoire passe par la
structure C suivante :
<programlisting>typedef struct Complex {
double x;
double y;
} Complex;
</programlisting>
Ce type ne pouvant tenir sur une simple valeur <type>Datum</type>, il sera passé
par référence.
</para>
<para>
La représentation externe du type se fera sous la forme de la chaîne
<literal>(x,y)</literal>.
</para>
<para>
En général, les fonctions d'entrée et de sortie ne sont pas compliquées à
écrire, particulièrement la fonction de sortie.
Mais lors de la définition de la représentation externe du type par une
chaîne de caractères, il faudra peut-être écrire un analyseur complet et
robuste, comme fonction d'entrée, pour cette représentation. Par exemple :
<programlisting><![CDATA[PG_FUNCTION_INFO_V1(complex_in);
Datum
complex_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
double x,
y;
Complex *result;
if (sscanf(str, " ( %lf , %lf )", &x, &y) != 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for complex: \"%s\"",
str)));
result = (Complex *) palloc(sizeof(Complex));
result->x = x;
result->y = y;
PG_RETURN_POINTER(result);
}
]]></programlisting>
La fonction de sortie peut s'écrire simplement :
<programlisting><![CDATA[PG_FUNCTION_INFO_V1(complex_out);
Datum
complex_out(PG_FUNCTION_ARGS)
{
Complex *complex = (Complex *) PG_GETARG_POINTER(0);
char *result;
result = (char *) palloc(100);
snprintf(result, 100, "(%g,%g)", complex->x, complex->y);
PG_RETURN_CSTRING(result);
}
]]></programlisting>
</para>
<para>
Il est particulièrement important de veiller à ce que les fonctions d'entrée
et de sortie soient bien inversées l'une par rapport à l'autre. Dans le cas
contraire, de grosses difficultés pourraient apparaître lors de la sauvegarde
de la base dans un fichier en vue d'une future relecture de ce fichier.
Ceci est un problème particulièrement fréquent lorsque des nombres
à virgule flottante entrent en jeu.
</para>
<para>
De manière optionnelle, un type utilisateur peut fournir des
routines d'entrée et de sortie binaires. Les entrées/sorties binaires sont
normalement plus rapides mais moins portables que les entrées/sorties
textuelles. Comme avec les entrées/sorties textuelles, c'est l'utilisateur
qui définit précisément la représentation binaire externe. La plupart des
types de données intégrés tentent de fournir une représentation binaire
indépendante de la machine. Dans le cas du type <type>complex</type>,
des convertisseurs d'entrées/sorties binaires pour le type
<type>float8</type> sont utilisés :
<programlisting><![CDATA[PG_FUNCTION_INFO_V1(complex_recv);
Datum
complex_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
Complex *result;
result = (Complex *) palloc(sizeof(Complex));
result->x = pq_getmsgfloat8(buf);
result->y = pq_getmsgfloat8(buf);
PG_RETURN_POINTER(result);
}
PG_FUNCTION_INFO_V1(complex_send);
Datum
complex_send(PG_FUNCTION_ARGS)
{
Complex *complex = (Complex *) PG_GETARG_POINTER(0);
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendfloat8(&buf, complex->x);
pq_sendfloat8(&buf, complex->y);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
]]></programlisting>
</para>
<para>
Lorsque les fonctions d'entrée/sortie sont écrites et
compilées en une bibliothèque partagée, le type
<type>complex</type> peut être défini en SQL. Tout d'abord,
il est déclaré comme un type shell :
<programlisting>CREATE TYPE complex;
</programlisting>
Ceci sert de paramètre qui permet de mettre en référence le type pendant
la définition de ses fonctions E/S. Les fonctions E/S peuvent alors être
définies :
<programlisting>CREATE FUNCTION complex_in(cstring)
RETURNS complex
AS '<replaceable>filename</replaceable>'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION complex_out(complex)
RETURNS cstring
AS '<replaceable>filename</replaceable>'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION complex_recv(internal)
RETURNS complex
AS '<replaceable>filename</replaceable>'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION complex_send(complex)
RETURNS bytea
AS '<replaceable>filename</replaceable>'
LANGUAGE C IMMUTABLE STRICT;
</programlisting>
</para>
<para>
La définition du type de données peut ensuite être fournie complètement :
<programlisting>CREATE TYPE complex (
internallength = 16,
input = complex_in,
output = complex_out,
receive = complex_recv,
send = complex_send,
alignment = double
);
</programlisting>
</para>
<para>
Quand un nouveau type de base est défini,
<productname>PostgreSQL</productname> fournit automatiquement le support pour
des tableaux de ce type.
<indexterm>
<primary>tableau</primary>
<secondary>types utilisateur</secondary>
</indexterm>
Le type tableau a habituellement le nom du type de base
préfixé par un caractère souligné (<literal>_</literal>).
</para>
<para>
Lorsque le type de données existe, il est possible de déclarer les fonctions
supplémentaires de définition des opérations utiles pour ce type.
Les opérateurs peuvent alors être définis par dessus ces fonctions et, si
nécessaire, des classes d'opérateurs peuvent être créées pour
le support de l'indexage du type de données. Ces couches supplémentaires sont
discutées dans les sections suivantes.
</para>
<para>
<indexterm>
<primary>TOAST</primary>
<secondary>types utilisateur</secondary>
</indexterm>
Si les valeurs du type de données varient en taille (sous la forme interne),
le type de données doit être marqué comme TOAST-able (voir
<xref linkend="storage-toast"/>). Vous devez le faire même si les données sont
trop petites pour être compressées ou stockées en externe car
<acronym>TOAST</acronym> peut aussi gagner de la place sur des petites
données en réduisant la surcharge de l'en-tête.
</para>
<para>
Pour cela, la représentation interne doit suivre la disposition standard
pour les données de longueur variable : les quatre premiers octets
doivent être un champ <type>char[4]</type> dont l'accès n'est jamais
direct (appelé <structfield>vl_len_</structfield>). Vous devez utiliser
<function>SET_VARSIZE()</function> pour stocker la taille du datum dans ce
champ et <function>VARSIZE()</function> pour le récupérer. Les fonctions C
opérant sur ce type de données doivent toujours faire attention à déballer
toutes valeurs TOAST qu'elles récupèrent en utilisant
<function>PG_DETOAST_DATUM</function> (ce détail est habituellement caché
en définissant des macros <function>GETARG_DATATYPE_P</function> spécifiques
au type). Ensuite, lors de l'exécution de la commande <command>CREATE
TYPE</command>, précisez la longueur interne comme <literal>variable</literal>
et sélectionnez l'option de stockage approprié.
</para>
<para>
Si l'alignement n'est pas important (soit seulement pour une fonction
spécifique soit parce que le type de données spécifie un alignement par
octet), alors il est possible d'éviter
<function>PG_DETOAST_DATUM</function>. Vous pouvez utiliser
<function>PG_DETOAST_DATUM_PACKED</function> à la place (habituellement
caché par une macro <function>GETARG_DATATYPE_PP</function>) et utiliser les
macros <function>VARSIZE_ANY_EXHDR</function> et
<function>VARDATA_ANY</function> pour accéder à un datum potentiellement
packagé.
Encore une fois, les données renvoyées par ces macros ne sont pas alignées
même si la définition du type de données indique un alignement. Si
l'alignement est important pour vous, vous devez passer par l'interface
habituelle, <function>PG_DETOAST_DATUM</function>.
</para>
<note>
<para>
Un ancien code déclare fréquemment <structfield>vl_len_</structfield>
comme un champ de type <type>int32</type> au lieu de
<type>char[4]</type>. C'est correct tant que la définition de la structure
a d'autres champs qui ont au moins un alignement <type>int32</type>.
Mais il est dangereux d'utiliser une telle définition de structure en
travaillant avec un datum potentiellement mal aligné ; le compilateur
peut le prendre comme une indication pour supposer que le datum est en
fait aligné, ceci amenant des « core dump » sur des architectures qui
sont strictes sur l'alignement.
</para>
</note>
<para>
Pour plus de détails, voir la description de la commande
<xref linkend="sql-createtype" endterm="sql-createtype-title"/>.
</para>
</sect1>