31.11. Types d�finis par l'utilisateur

D�crit dans la Section 31.2, PostgreSQL peut �tre �tendu pour supporter de nouveaux types de donn�es. Cette section d�crit comment d�finir de nouveaux types de base, qui sont des types de donn�es d�finis en-dessous du niveau du langage SQL. Cr�er un nouveau type de base requiert l'impl�mentation de fonctions pour op�rer sur le type dans un langage de base du niveau du C.

Les exemples de cette section sont disponibles dans complex.sql et complex.c du r�pertoire src/tutorial de la distribution des sources. Voir le fichier README de ce r�pertoire pour les instructions d'ex�cution des exemples.

Un type d�fini par l'utilisateur doit toujours avoir des fonctions d'entr�e et de sortie. Ces fonctions d�terminent comment le type appara�t dans les cha�nes de caract�res (pour l'entr�e par l'utilisateur et le renvoi � l'utilisateur) et comment ce type est organis� en m�moire. La fonction d'entr�e prend comme argument une cha�ne de caract�res termin�e par NULL et renvoie la repr�sentation interne (en m�moire) du type. La fonction de sortie prend comme argument la repr�sentation interne du type et renvoie une cha�ne de caract�res termin�e par NULL. Si vous voulez faire plus avec le type que simplement l'enregistrer, vous devez apporter des fonctions suppl�mentaires pour impl�menter toutes op�rations que vous souhaitez avoir pour ce type.

Supposons que nous voulions d�finir un type complex repr�sentant les nombres complexes. Une fa�on naturelle de repr�senter un nombre complexe en m�moire serait la structure C suivante :

typedef struct Complex {
    double      x;
    double      y;
} Complex;

Nous aurons besoin d'utiliser ce type par r�f�rence car il est trop important pour tenir sur une seule valeur Datum.

Comme repr�sentation externe du type sous forme de cha�ne, nous choisissons une cha�ne de la forme (x,y).

Habituellement, les fonctions d'entr�e et de sortie ne sont pas compliqu�es � �crire, surtout la fonction de sortie. Mais en d�finissant la repr�sentation externe du type par une cha�ne, souvenez-vous que vous devez �ventuellement �crire un analyseur complet et robuste pour cette repr�sentation en tant que fonction d'entr�e. Par exemple :

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);
}

La fonction de sortie peut simplement s'�crire :

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);
}

Vous devriez faire attention en �crivant des fonctions d'entr�e et de sortie inverses l'une de l'autre. Sinon, vous aurez de graves probl�mes quand vous aurez besoin de sauvegarder votre base de donn�es dans un fichier et ensuite de le relire. Ceci est un probl�me particuli�rement fr�quent quand des nombres � virgule flottante sont concern�s.

De mani�re optionnelle, un type d�fini par l'utilisateur peut apporter 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. Avec les entr�es/sorties textuelles, c'est � vous de d�finir exactement la repr�sentation binaire externe. La plupart des types de donn�es int�gr�s essaient d'apporter une repr�sentation binaire ind�pendante de la machine. Pour complex, nous allons revenir aux convertisseurs d'entr�es/sorties binaires pour le type float8 :

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));
}

Pour d�finir le type complex, nous avons besoin de cr�er les fonctions d'entr�es/sorties d�finies par l'utilisateur avant de cr�er le type :

CREATE FUNCTION complex_in(cstring)
    RETURNS complex
    AS 'filename'
    LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION complex_out(complex)
    RETURNS cstring
    AS 'filename'
    LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION complex_recv(internal)
   RETURNS complex
   AS 'filename'
   LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION complex_send(complex)
   RETURNS bytea
   AS 'filename'
   LANGUAGE C IMMUTABLE STRICT;

Notez que la d�claration des fonctions d'entr�e et de sortie doit pouvoir r�f�rencer un type non encore d�fini. Ceci est permis mais provoque des messages d'avertissement qui peuvent �tre ignor�s. La fonction en entr�e doit d'abord appara�tre.

Finalement, nous pouvons d�clarer le type de donn�es :

CREATE TYPE complex (
   internallength = 16, 
   input = complex_in,
   output = complex_out,
   receive = complex_recv,
   send = complex_send,
   alignment = double
);

Quand vous d�finissez un nouveau type de base, PostgreSQL fournit automatiquement le support pour des tableaux de ce type. Pour des raisons historiques, le type tableau a le m�me nom que le type de base avec un caract�re soulign� (_) en pr�fixe.

Une fois que le type de donn�es existe, nous pouvons d�clarer les fonctions suppl�mentaires pour apporter des op�rations utiles pour ce type de donn�es. Les op�rateurs peuvent alors �tre d�finis au-dessus de ces fonctions et, si n�cessaire, les classes d'op�rateurs peuvent aussi �tre cr��es pour apporter le support de l'indexage du type de donn�es. Ces couches suppl�mentaires sont discut�es dans les sections suivantes.

Si les valeurs de votre type de donn�e peuvent exc�der une taille de quelques centaines d'octets (sous la forme interne), vous devriez marquer le type de donn�es comme TOAST-able. Pour cela, la repr�sentation interne doit suivre le cadre standard des donn�es � longueur variable : les quatre premiers octets doivent �tre un int32 contenant la longueur totale en octets de la donn�e (lui-m�me inclus). Les fonctions C op�rant sur le type de donn�es doivent faire bien attention � d�baller toutes les valeurs toast des donn�es (ce d�tail peut normalement �tre cach�e dans les macros GETARG). Puis, quand on ex�cute la commande CREATE TYPE, sp�cifiez la longueur interne comme variable et choisissez l'option de stockage en m�moire appropri�e.

Pour plus de d�tails, voir la description de la commande CREATE TYPE.