30.6. Utiliser des variables hôtes

Dans la Section 30.4, 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.

30.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. Dans l'environnement SQL, nous appelons les références à des variables C des variables hôtes.

30.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;
char  foo[16], bar[16];

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.

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.

30.6.3. 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.

30.6.4. Indicateurs

Les exemples ci-dessus ne gèrent pas les valeurs nulles (NULL). En fait, ces exemples de récupération afficheront une erreur s'ils récupèrent une valeur nulle à partir de la base de données. Pour être capable de passer des valeurs nulles à la base de données ou de récupérer des valeurs nulles 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 nulle, 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 nulles :

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 nulle et elle sera négative si la valeur est nulle.

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