Un type composite représente la structure d'une ligne ou d'un enregistrement ; il est en essence une simple liste de noms de champs et de leur types de données. PostgreSQL™ autorise l'utilisation de types composite identiques de plusieurs façons à l'utilisation des types simples. Par exemple, une colonne d'une table peut être déclarée comme étant de type composite.
Voici deux exemples simples de définition de types composite :
CREATE TYPE complexe AS ( r double precision, i double precision ); CREATE TYPE element_inventaire AS ( nom text, id_fournisseur integer, prix numeric );
La syntaxe est comparable à CREATE TABLE sauf que seuls les noms de champs et leur types peuvent être spécifiés ; aucune contrainte (telle que NOT NULL) ne peut être inclus actuellement. Notez que le mot clé AS est essentiel ; sans lui, le système penserait à un autre genre de commande CREATE TYPE et vous obtiendriez d'étranges erreurs de syntaxe.
Après avoir défini les types, nous pouvons les utiliser pour créer des tables :
CREATE TABLE disponible ( element element_inventaire, nombre integer ); INSERT INTO disponible VALUES (ROW('fuzzy dice', 42, 1.99), 1000);
ou des fonctions :
CREATE FUNCTION prix_extension(element_inventaire, integer) RETURNS numeric AS 'SELECT $1.prix * $2' LANGUAGE SQL; SELECT prix_extension(element, 10) FROM disponible;
Quand vous créez une table, un type composite est automatiquement créé, avec le même nom que la table, pour représenter le type de ligne de la table. Par exemple, si nous avions dit :
CREATE TABLE element_inventaire ( nom text, id_fournisseur integer REFERENCES fournisseur, prix numeric CHECK (prix > 0) );
alors le même type composite element_inventaire montré ci-dessus aurait été créé et pourrait être utilisé comme ci-dessus. Néanmoins, notez une restriction importante de l'implémentation actuelle : comme aucune contrainte n'est associée avec un type composite, les contraintes indiquées dans la définition de la table ne sont pas appliquées aux valeurs du type composite en dehors de la table. (Un contournement partiel est d'utiliser les types de domaine comme membres de types composites.)
Pour écrire une valeur composite comme une constante littérale, englobez les valeurs du champ dans des parenthèses et séparez-les par des virgules. Vous pouvez placer des guillemets doubles autour de chaque valeur de champ et vous devez le faire si elle contient des virgules ou des parenthèses (plus de détails ci-dessous). Donc, le format général d'une constante composite est le suivant :
'( val1 , val2 , ... )'
Voici un exemple :
'("fuzzy dice",42,1.99)'
qui serait une valeur valide du type element_inventaire défini ci-dessus. Pour rendre un champ NULL, n'écrivez aucun caractère dans sa position dans la liste. Par exemple, cette constante spécifie un troisième champ NULL :
'("fuzzy dice",42,)'
Si vous voulez un champ vide au lieu d'une valeur NULL, saisissez deux guillemets :
'("",42,)'
Ici, le premier champ est une chaîne vide non NULL alors que le troisième est NULL.
(Ces constantes sont réellement seulement un cas spécial de constantes génériques de type discutées dans la Section 4.1.2.7, « Constantes d'autres types ». La constante est initialement traitée comme une chaîne et passée à la routine de conversion de l'entrée de type composite. Une spécification explicite de type pourrait être nécessaire.)
La syntaxe d'expression ROW pourrait aussi être utilisée pour construire des valeurs composites. Dans la plupart des cas, ceci est considérablement plus simple à utiliser que la syntaxe de chaîne littérale car vous n'avez pas à vous inquiéter des multiples couches de guillemets. Nous avons déjà utilisé cette méthode ci-dessus :
ROW('fuzzy dice', 42, 1.99) ROW('', 42, NULL)
Le mot clé ROW est optionnel si vous avez plus d'un champ dans l'expression, donc ceci peut être simplifié avec
('fuzzy dice', 42, 1.99) ('', 42, NULL)
La syntaxe de l'expression ROW est discutée avec plus de détails dans la Section 4.2.13, « Constructeurs de lignes ».
Pour accéder à un champ d'une colonne composite, vous pouvez écrire un point et le nom du champ, un peu comme la sélection d'un champ à partir d'un nom de table. En fait, c'est tellement similaire que vous pouvez souvent utiliser des parenthèses pour éviter une confusion de l'analyseur. Par exemple, vous pouvez essayer de sélectionner des sous-champs à partir de notre exemple de table, disponible, avec quelque chose comme :
SELECT element.nom FROM disponible WHERE element.prix > 9.99;
Ceci ne fonctionnera pas car le nom element est pris pour le nom d'une table, et non pas d'une colonne de disponible, suivant les règles de la syntaxe SQL. Vous devez l'écrire ainsi :
SELECT (element).nom FROM disponible WHERE (element).prix > 9.99;
ou si vous avez aussi besoin d'utiliser le nom de la table (par exemple dans une requête multi-table), de cette façon :
SELECT (disponible.element).nom FROM disponible WHERE (disponible.element).prix > 9.99;
Maintenant, l'objet entre parenthèses est correctement interprété comme une référence à la colonne element, puis le sous-champ peut être sélectionné à partir de lui.
Des problèmes syntaxiques similaires s'appliquent quand vous sélectionnez un champ à partir d'une valeur composite. En fait, pour sélectionner un seul champ à partir du résultat d'une fonction renvoyant une valeur composite, vous aurez besoin d'écrire quelque chose comme :
SELECT (ma_fonction(...)).champ FROM ...
Sans les parenthèses supplémentaires, ceci provoquera une erreur.
Voici quelques exemples de la bonne syntaxe pour insérer et mettre à jour des colonnes composites. Tout d'abord pour insérer ou modifier une colonne entière :
INSERT INTO matab (col_complexe) VALUES((1.1,2.2)); UPDATE matab SET col_complexe = ROW(1.1,2.2) WHERE ...;
Le premier exemple omet ROW, le deuxième l'utilise ; nous pouvons le faire des deux façons.
Nous pouvons mettre à jour un sous-champ individuel d'une colonne composite :
UPDATE matab SET col_complexe.r = (col_complexe).r + 1 WHERE ...;
Notez ici que nous n'avons pas besoin de (et, en fait, ne pouvons pas) placer des parenthèses autour des noms de colonnes apparaissant juste après SET, mais nous avons besoin de parenthèses lors de la référence à la même colonne dans l'expression à droite du signe d'égalité.
Et nous pouvons aussi spécifier des sous-champs comme cibles de la commande INSERT :
INSERT INTO matab (col_complexe.r, col_complexe.i) VALUES(1.1, 2.2);
Si tous les sous-champs d'une colonne ne sont pas spécifiés, ils sont remplis avec une valeur NULL.
La représentation texte externe d'une valeur composite consiste en des éléments qui sont interprétés suivant les règles de conversion d'entrées/sorties pour les types de champs individuels, plus des décorations indiquant la structure composite. Cette décoration consiste en des parenthèses (( et )) autour de la valeur entière ainsi que des virgules (,) entre les éléments adjacents. Des espace blancs en dehors des parenthèses sont ignorés mais à l'intérieur des parenthèses, ils sont considérés comme faisant partie de la valeur du champ et pourrait ou non être significatif suivant les règles de conversion de l'entrée pour le type de données du champ. Par exemple, dans :
'( 42)'
l'espace blanc sera ignoré si le type du champ est un entier, mais pas s'il s'agit d'un champ de type texte.
Comme indiqué précédemment, lors de l'écriture d'une valeur composite, vous pouvez utiliser des guillemets doubles autour de chaque valeur de champ individuel. Vous devez le faire si la valeur du champ pourrait sinon gêner l'analyseur de la valeur du champ composite. En particulier, les champs contenant des parenthèses, des virgules, des guillemets doubles ou des antislashs doivent être entre guillemets doubles. Pour placer un guillemet double ou un antislash dans la valeur d'un champ composite entre guillemets, faites-le précéder d'un antislash. (De plus, une paire de guillemets doubles à l'intérieur d'une valeur de champ à guillemets doubles est pris pour représenter un caractère guillemet double, en analogie aux règles des guillemets simples dans les chaînes SQL littérales.) Autrement, vous pouvez éviter les guillemets et utiliser l'échappement par antislash pour protéger tous les caractères de données qui auraient été pris pour une syntaxe composite.
Une valeur de champ composite vide (aucun caractère entre les virgules ou parenthèses) représente une valeur NULL. Pour écrire une valeur qui est une chaîne vide plutôt qu'une valeur NULL, écrivez "".
La routine de sortie composite placera des guillemets doubles autour des valeurs de champs s'ils sont des chaînes vides ou s'ils contiennent des parenthèses, virgules, guillemets doubles, antislash ou espaces blancs. (Faire ainsi pour les espaces blancs n'est pas essentiel mais aide à la lecture.) Les guillemets doubles et antislashs dans les valeurs des champs seront doublés.
Rappelez-vous que ce que vous allez saisir dans une commande SQL sera tout d'abord interprété comme une chaîne littérale, puis comme un composite. Ceci double le nombre d'antislash dont vous avez besoin (en supposant que la syntaxe d'échappement des chaînes est utilisée). Par exemple, pour insérer un champ text contenant un guillemet double et un antislash dans une valeur composite, vous devez écrire :
INSERT ... VALUES (E'("\\"\\\\")');
Le processeur des chaînes littérales supprime un niveau d'antislash de façon à ce qui arrive à l'analyseur de valeurs composites ressemble à ("\"\\"). À son tour, la chaîne remplie par la routine d'entrée du type de données text devient "\. (Si nous étions en train de travailler avec un type de données dont la routine d'entrée traite aussi les antislashs spécialement, bytea par exemple, nous pourrions avoir besoin d'au plus huit antislashs dans la commande pour obtenir un antislash dans le champ composite stocké.) Le guillemet dollar (voir Section 4.1.2.4, « Constantes de chaînes avec guillemet dollar ») pourrait être utilisé pour éviter le besoin des antislashs doublés.
La syntaxe du constructeur ROW est habituellement plus simple à utiliser que la syntaxe du littérale composite lors de l'écriture de valeurs composites dans des commandes SQL. Dans ROW, les valeurs individuelles d'un champ sont écrits de la même façon qu'ils l'auraient été en étant pas membres du composite.