31.6. Utiliser des variables hôtes
La Section 31.4,
« Exécuter des commandes SQL » présentait l'exécution
d'instructions SQL à partir d'un programme SQL embarqué. Certaines 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étails l'échange de données entre
le programme C et les instructions SQL embarquées à l'aide d'un
mécanisme simple appelé variables hôtes.
Dans un programme SQL embarqué, les instructions SQL sont considérées
comme invitées dans le code du programme C
qui est le langage hôte. Du coup, les
variables du programme C sont appelées variables hôtes.
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 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ètres 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 trouve des déclarations normales de variables
C, comme
int x = 4;
char foo[16], bar[16];
Une valeur initiale optionnelle peut être affectée à la variable.
La portée de la variable est déterminée par son emplacement dans la
section de déclaration du programme. Les variables peuvent aussi
être déclarées 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 nécessaire 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 peut pas
gérer ces types.
31.6.3. Les différents types de variables hôtes
Des tableaux (
array
), définitions de type
(
typedef
), structures (
struct
) et
pointeurs (
pointer
) peuvent aussi être utilisés
comme varibale hôte. Il existe également des types spéciaux de
variables hôtes qui n'existent qu'en ECPG.
Quelques exemples sur les variables hôtes :
-
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;
C'est à l'utilisateur de gérer la longueur du tableau. Si une
telle variable hôte est utilisée comme variable cible d'une
requête qui renvoie une chaîne de plus de 49 caractères, un
dépassement de tampon survient.
-
Typedefs
-
Le mot clé typedef est utilisé pour
faire correspondre de nouveaux types à des types déjà
existants.
EXEC SQL BEGIN DECLARE SECTION;
typedef char montypecaractere[40];
typedef long serial_t;
EXEC SQL END DECLARE SECTION;
On peut 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
-
Des pointeurs peuvent être déclarés vers les types les plus
communs. Néanmoins, Les pointeurs en tant que variables
cibles de requêtes ne peuvent être utilisés sans allocation
automatique. Voir Section 31.10,
« Utiliser les zones de descripteur 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 facilitent
l'interaction avec les données du serveur SQL. Par exemple,
le support des types varchar,
numeric, date, timestamp et
interval a été implanté. Section 31.8,
« Bibliothèque pgtypes » contient des fonctions
basiques de gestion de ces types. Il n'est ainsi pas
nécessaire 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 en 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 convient pour interfacer des données SQL de
type varchar.
31.6.4. SELECT INTO et FETCH INTO
Les sections précédentes expliquent le passage de données entre
l'application et une commande SQL. Pour récupérer le résultat d'une
requête, le SQL embarqué fournit des variantes spéciales des
commandes habituelles
SELECT
et
FETCH
. Ces commandes ont une
clause INTO particulière qui indique les
variables hôtes de stockage des valeurs récupérées.
Voici un exemple :
/*
* Soit la table :
* 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 la
liste de sélection et la clause FROM. Le
nombre d'éléments dans la liste du
select
et dans la liste qui suit
INTO (aussi appelée liste cible) doivent
être identiques.
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 affichent 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. 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 vaut zéro
si la valeur n'est pas nulle, elle est négative dans le cas
contraire.
L'indicateur a une autre fonction : si la valeur de l'indicateur
est positive, cela signifie que la valeur n'est pas nulle mais
qu'elle a été tronquée lors de son stockage dans la variable hôte.