31.6. Utiliser des variables hôtes

Dans la Section 31.4, « Exécuter des commandes SQL », nous avons vu comment exécuter des instructions SQL à partir d'un programme SQL embarqué. Quelques-unes 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étail comment échanger des données entre votre programme C et les instructions SQL embarquées en utilisant un mécanisme simple appelé variables hôtes. Dans un programme SQL embarqué, nous considérons les instructions SQL comme invités dans le code du programme C qui est le langage hôte. Du coup, les variables du programme C sont appelées variables hôte.

31.6.1. Aperçu

Échanger des 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 :

EXEC SQL INSERT INTO unetable VALUES (:v1, 'foo', :v2);

Cette instruction fait référence à deux variables C nommées v1 et v2, et utilise également une chaîne littérale SQL pour illustrer l'absence de restriction à l'utilisation d'un type de données ou d'un autre.

Ce style d'insertions de variables C dans des instructions SQL fonctionne dans tous les cas où l'on attend une expression de valeur dans une instruction SQL.

31.6.2. Sections de déclaration

Pour passer des données du programme à la base de données, comme paramètre 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.

Cette section commence avec

EXEC SQL BEGIN DECLARE SECTION;

et se termine avec

EXEC SQL END DECLARE SECTION;

Entre ces lignes, on trouvera des déclarations normales de variables C, comme

int   x = 4;
char  foo[16], bar[16];

Comme vous pouvez le constater, vous pouvez affecter en option une valeur initiale à la variable. La portée de la variable est déterminée par son emplacement dans la section de déclaration du programme. Vous pouvez aussi déclarer des variables avec la syntaxe suivante qui crée implicitement une section de déclaration :

EXEC SQL int i = 4;

Il peut y avoir autant de sections de déclarations dans un programme que souhaité.

Les déclarations sont aussi placées dans le fichier de sortie comme des variables C normales. Du coup, il n'est plus besoin 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.

La définition d'une structure ou union doit aussi être saisie dans une section DECLARE. Sinon, le préprocesseur, ne connaissant pas leur définition, ne pourra pas gérer ces types.

31.6.3. Différents types de variables hôte

Comme variable hôte, vous pouvez aussi utiliser des tableaux, définitions de type, structures et pointeurs. De plus, il y a des types spéciaux de variables hôte qui existent seulement dans ECPG.

Quelques exemples sur les variables hôte :

Tableaux

Une des utilisations les plus communes d'une déclaration de tableaux est certainement l'allocation d'un tableau de caractères comme dans

EXEC SQL BEGIN DECLARE SECTION;
    char chaine[50];
EXEC SQL END DECLARE SECTION;

Notez que vous devez prendre soin de la longueur vous-même. Si vous utilisez cette variable hôte comme variable cible d'une requête qui renvoie une chaîne de plus de 49 caractères, un dépassement de tampons survient.

Typedefs

Utilisez le mot clé typedef pour faire correspondre les nouveaux types aux types déjà existants.

EXEC SQL BEGIN DECLARE SECTION;
    typedef char montypecaractere[40];
    typedef long serial_t;
EXEC SQL END DECLARE SECTION;

Notez que vous pouvez aussi utiliser :

EXEC SQL TYPE serial_t IS long;

Cette déclaration n'a pas besoin de faire partie d'une section de déclaration.

Pointers

Vous pouvez déclarer des pointeurs vers les types les plus communs. Néanmoins, notez que vous ne pouvez pas utiliser des pointeurs en tant que variables cibles des requêtes sans allocation automatique. Voir Section 31.10, « Utiliser les zones des descripteurs SQL » pour plus d'informations sur l'allocation automatique.

EXEC SQL BEGIN DECLARE SECTION;
    int   *intp;
    char **charp;
EXEC SQL END DECLARE SECTION;
Types spéciaux de variables

ECPG contient certains types spéciaux qui vous aident à interagir facilement avec les données du serveur SQL. Par exemple, le support des types varchar, numeric, date, timestamp et interval a été implémenté. Section 31.8, « Bibliothèque pgtypes » contient des fontions basiques de gestion de ces types pour que vous n'ayez pas besoin d'envoyer une requête au serveur SQL simplement pour ajouter un interval à une variable de type timestamp par exemple.

Le type spécial VARCHAR est converti dans une struct nommée pour chaque variable. Une déclaration telle que

VARCHAR var[180];

est convertie en

struct varchar_var { int len; char arr[180]; } var;

Cette structure est utilisable pour créer une interface des données SQL de type varchar.

31.6.4. SELECT INTO et FETCH INTO

Nous savons maintenant insérer des données engendrées par un programme dans une commande SQL. Mais comment récupérer les résultats d'une requête ? Dans ce but, le SQL embarqué fournit des variantes spéciales des commandes habituelles SELECT et FETCH. Ces commandes ont une clause INTO particulière qui spécifie les variables hôtes dans lesquelles seront stockées les valeurs récupérées.

Voici un exemple :

/*
 * Soit la table suivante :
 * 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;

La clause INTO apparaît donc entre les champs du select et la clause FROM. Le nombre d'éléments dans la liste du select et celui de la liste après INTO (aussi appelée liste cible) doivent être identiques.

Voici un exemple utilisant la commande FETCH :

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 (...);

Ici, la clause INTO apparaît après toutes les autres clauses.

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.

31.6.5. Indicateurs

Les exemples ci-dessus ne gèrent pas les valeurs NULL. En fait, ces exemples de récupération afficheront 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'indicateur et contient un drapeau indiquant si la valeur est NULL, auquel cas la valeur de la variable hôte réelle est ignorée. Voici un exemple qui gère correctement la récupération de valeurs NULL :

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;

La variable indicateur val_ind vaudra zéro si la valeur est non NULL et elle sera négative si la valeur est NULL.

L'indicateur a une autre fonction : si la valeur de l'indicateur est positive, cela signifie que la valeur est non NULL mais qu'elle a été tronquée lors de son stockage dans la variable hôte.