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.
É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.
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.
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 :
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.
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.
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;
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.
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.
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.