L`accès à vos données avec ODBC en C

publicité
L'accès à vos données avec ODBC en C
Date de publication : 07 août 2008
Par Jessee Edouard (Accueil)
Le but de ce tutoriel est de vous initier à la programmation d'applications frontales de base de
données en langage C en utilisant la technologie ODBC. La maîtrise du langage C, du langage
SQL et d'au moins un SGBD (Access, MySQL, Oracle, ...) est donc évidemment requise.
Aucun rappel ne sera effectué sur ces trois points.
Commentez cet article : 1 commentaire ·
Version PDF Version hors-ligne
I. Introduction
II. Notions fondamentales
II-A. Les pilotes ODBC
II-B. Les sources de données ODBC
II-C. Le gestionnaire de pilotes ODBC
II-D. Les versions
III. Créer une source de données sous Windows
IV. Programmation
IV-A. La base de données
IV-B. Création d'une simple application
IV-C. Amélioration
IV-D. Une autre méthode : attacher les colonnes
V. Analyse des résultats
VI. Les diagnostics
VII. Se connecter de différentes manières
VII-A. La fonction SQLDriverConnect
VII-B. Les paramètres
VII-C. La chaîne de connexion
VIII. Connaître la structure d'une base de données
VIII-A. La fonction SQLTables
VIII-B. La fonction SQLColumns
IX. Conclusion
I. Introduction
ODBC (Open DataBase Connectivity) est une interface indépendante de tout système
d'exploitation (elle est portable) et de tout langage de programmation (elle est ouverte)
permettant de se connecter à n'importe quel serveur de base de données (même Access ou
Excel, etc.) et ce de manière standard. Fondamentalement, pour accéder à une base de
données, le programmeur est censé utiliser les APIs du SGBD utilisé (ou tout au moins un
wrapper). Cela présente un inconvénient majeur : elle rend les programmes dépendant du
SGBD. Par contre, c'est la seule solution lorsqu'on souhaite accéder à toutes les
fonctionnalités du logiciel, mais cela est loin de nos objectifs. ODBC est une interface qui
cache les détails de chaque SGBD. Pour être un peu plus exact, ODBC est une spécification
d'une API de SGBD, elle-même basée sur deux autres spécifications connues sous le nom de
X/Open et ISO CLI, formellement X/Open CAE "SQL Call-Level Interface" et ISO/IEC 90753:1995 (E) "Call-Level Interface".
Dans ce tutoriel, nous allons découvrir comment utiliser ODBC sous Windows en langage C
mais, comme nous l'avons déjà dit tout à l'heure, ODBC existe sur de nombreux systèmes
incluant Windows, MacOS et UNIX, ce qui implique que ce tutoriel vous est quand même
aussi destiné même si vous n'êtes pas sous Windows.
II. Notions fondamentales
II-A. Les pilotes ODBC
ODBC fonctionne grâce à des pilotes, appelés justement pilotes ODBC, qui convertissent les
appels ODBC en appels natifs pour le SGBD. Pour accéder à une base de données en utilisant
ODBC, il faut donc qu'un pilote adéquat soit installé. Ce pilote est généralement
téléchargeable sur le site du constructeur s'il n'est pas déjà installé. Si vous êtes sous
Windows, vous n'avez pas besoin d'installer les pilotes pour Oracle, Access, dBase, Excel,
SQL Server et bien d'autres encore puisqu'ils ont déjà été installés en même temps que
Windows, à moins que vous vouliez tout simplement héberger plusieurs pilotes sur votre
ordinateur ou pour n'importe quelle autre raison.
II-B. Les sources de données ODBC
Une source de données ODBC est en quelque sorte un lien permettant à une application
utilisant ODBC de se connecter à la base de données décrite dans cette source de données.
Une source de données représente donc une base de données ainsi que le pilote qu'il faut
utiliser pour s'y connecter. La création d'une source de données se fait en quelques clics (ou
en ligne de commandes ...) en utilisant un utilitaire généralement fourni par le constructeur
(normalement fourni en même temps que le pilote). Les paramètres à spécifier lors de la
création d'une source de données varient selon le SGBD. Il s'agit de paramètres comme
l'emplacement du serveur, le port sur lequel il faut se connecter, le nom de la base, etc. mais
aussi et surtout le nom de la source de données appelée aussi DSN pour Data Source Name.
C'est ce nom qu'il faudra spécifier en argument de certaines fonctions comme SQLConnect
qui permet de se connecter à une base de données par exemple.
II-C. Le gestionnaire de pilotes ODBC
Les applications qui utilisent ODBC ne se lient pas directement à un pilote ODBC mais au
gestionnaire de pilotes ODBC. Le gestionnaire de pilotes ODBC fournit des services (l'API
ODBC) aux applications en s'appuyant en grande partie sur des services fournis par les pilotes
ODBC. C'est le pilote qui communique réellement au SGBD (c'est-à-dire qui implémente les
appels) et fournit les résultats, s'il y en a, au gestionnaire, qui a son tour les transmettra à
l'application. Sous Windows, les applications utilisent odbc32.dll, le Gestionnaire de pilotes
ODBC de Microsoft. Sous les autres plateformes, on a unixODBC, le standard industriel
sous UNIX et iODBC (Independant ODBC), également disponible sous UNIX mais plus
populaire sous MacOS. Tous deux sont des projets Open Source. En particulier, unixODBC
est livré avec la plupart des distributions Linux et UNIX tandis que iODBC est installé sous
MacOS X depuis la version 10.2.
II-D. Les versions
L'API ODBC n'est pas restée la même depuis la version 1.0. Chaque nouvelle version a
amené des améliorations et de nouvelles fonctions par rapport à la version précédente. La
majorité des gestionnaires de pilotes ODBC sont actuellement conformes à la version 3.0
mais nombreux sont ceux qui supportent également une version 3.5x (un sur ensemble de la
version 3.0). Une des plus améliorations les plus importantes apportées par cette version par
rapport à la précédente est le support d'UNICODE. Dans ce tutoriel, nous-nous limiterons
cependant à la version 3.0, ainsi nos programmes pourront fonctionner aussi bien avec un
driver 3.0 qu'un driver 3.5x.
III. Créer une source de données sous
Windows
Windows permet d'administrer toutes les sources de données au sein d'une même interface qui
est l'Administrateur de sources de données ODBC. En outre, il permet également de voir la
liste de tous les pilotes installés. Une manière de l'ouvrir est d'aller dans Panneau de
configuration > Outils d'administration > Sources de données (ODBC), ou tout
simplement en exécutant directement le fichier odbcad32.exe. Pour ajouter une nouvelle
source de données, il suffit de cliquer sur Ajouter, de sélectionner le pilote à utiliser (ce qui
revient à selectionner le SGBD utilisé) puis de se laisser guider par l'assistant.
Sachez également que Windows distingue les sources de données utilisateurs visibles
uniquement pour l'utilisateur courant, les sources de données systèmes visibles pour tous les
utilisateurs et les sources de données fichiers qui sont représentées par un fichier. En
général, on préfèrera les sources de données utilisateurs ou systèmes.
IV. Programmation
IV-A. La base de données
Avant de commencer à programmer, créez d'abord une base de données que vous nommerez
pays_db contenant une table appelée pays_tbl constituée de deux champs : pays
(VARCHAR(14)) et capitale (VARCHAR(14)). Dans la suite, nous supposons qu'un utilisateur
nommé melem, utilisant le mot de passe 1234, ait accès à cette base. Vous remplacerez donc
ces nom et mot de passe par les vôtres ou rien (chaînes vides) si ce n'est pas requis. Créez
ensuite une source de données liée à cette base et nommez-la pays_dsn sans spécifier
d'utilisateur ni mot de passe par défaut afin que l'authentification soit obligatoire à chaque
connexion. Lorsque tout est prêt, vous pouvez continuer.
IV-B. Création d'une simple application
Pour utiliser ODBC sous Windows, il faut se lier avec odbc32.lib. En ce qui concerne le
fichiers d'en-tête, cela dépend des fonctions que l'on va utiliser mais dans tous les cas, il faut
tout d'abord inclure windows.h.
Une fois la source de données créée, se connecter à la base de données est relativement très
simple. Avant toute chose, il faut tout d'abord créer un environnement. A l'intérieur d'un
environnement, on pourra ensuite ouvrir une ou plusieurs connexions. Une fois la connexion
ouverte, on peut désormais envoyer des commandes (qui seront par exemple des requêtes
SQL) à la base de données.
Nous allons donc commencer par créer notre environnement, plus précisément : allouer un
handle d'environnement. Dans ODBC 3.x, la fonction à utiliser est SQLAllocHandle.
SQLRETURN SQLAllocHandle(SQLSMALLINT HandleType, SQLHANDLE
InputHandle, SQLHANDLE * OutputHandlePtr);
En premier argument, on doit spécifier le type du handle qu'on veut allouer. Dans notre cas il
s'agit d'un handle d'environnement donc on met SQL_HANDLE_ENV.
En deuxième argument, nous devons spécifier le « parent » du handle que l'on veut créer.
Dans notre cas, on doit passer NULL car un environnement ne peut pas avoir de parent (mais
un environnement par exemple est parent d'une éventuelle connexion).
En troisième argument, nous devons fournir l'adresse de la variable dans laquelle nous
voulons stocker le handle retourné.
Et enfin, cette fonction retourne comme toutes SQL_SUCCESS ou
SQL_SUCCESS_WITH_INFO en cas de succès, autre chose en cas d'erreur. La différence
entre SQL_SUCCESS et SQL_SUCCESS_WITH_INFO sera expliqué un peu plus bas mais
pour l'instant, sachez que pour tester si une fonction a réussi, il faut comparer sa valeur de
retour avec SQL_SUCCESS et SQL_SUCCESS_WITH_INFO. Une fonction a réussi si elle a
retourné SQL_SUCCESS ou SQL_SUCCESS_WITH_INFO. Heureusement, il existe une
macro qui fait bien cette comparaison, c'est la macro SQL_SUCCEEDED.
#include <windows.h> /* A inclure uniquement sous Windows */
#include <sql.h>
/* API ODBC de base
*/
#include <sqlext.h> /* Fonctions propres a ODBC 3.x
*/
int main()
{
SQLHENV env;
if ( !SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, NULL, &env))
)
/* SQLAllocHandle a echoue */ ;
else
{
/* On continue */
SQLFreeHandle(SQL_HANDLE_ENV, env);
}
return 0;
}
Ensuite, comme nous voulons utiliser ODBC 3.x (que ce soit 3.0 ou 3.5x) , nous devons
configurer notre environnement de manière à supporter cette version.
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
A l'intérieur d'un environnement, on peut créer autant de connexions que l'on souhaite (dans la
mesure où le système et/ou le SGBD le permet). Dans notre cas, nous allons créer une seule
connexion.
SQLHDBC con;
SQLAllocHandle(SQL_HANDLE_DBC, env, &con);
Et maintenant, la partie la plus intéressante, nous allons nous connecter à la base de données
représentée par la source de données pays_dsn, en utilisant le login melem et le mot de passe
1234. La fonction à utiliser est SQLConnect.
SQLCHAR dsn[] = "pays_dsn", uid[] = "melem", pwd[] = "1234";
if ( !SQL_SUCCEEDED(SQLConnect(con, dsn, SQL_NTS, uid, SQL_NTS, pwd,
SQL_NTS)) )
/* SQLConnect a echoue */ ;
else
{
/* On continue */
SQLDisconnect(con);
}
SQL_NTS signifie Null Terminated String, c'est-à-dire chaîne de caractères terminée par
zéro. En effet SQLConnect ne requiert pas que les chaînes passées en arguments doivent se
terminer par le caractère de fin de chaîne du C ('\0'), mais requiert le nombre de caractères de
chaque chaîne. SQL_NTS permet de lui indiquer qu'on a quand même pris le soin de mettre le
caractère nul en fin de chaîne, ce qui nous permet de passer des chaînes sans qu'on ait à
spécifier leur longueur. Lorsque la connexion a réussi, on peut désormais échanger des
données avec la base. Nous-nous proposons de récupérer (puis afficher) tous les
enregistrements de la table pays_tbl.
Il faut commencer par créer un handle d'un tampon qui va servir à contenir une requête ou le
résultat d'une requête. Nous utiliserons donc ce handle chaque fois que nous voulons exécuter
une requête puis pour récupérer le résultat de la requête.
SQLHSTMT stmt;
SQLAllocHandle(SQL_HANDLE_STMT, con, &stmt);
Pour exécuter notre requête, ce n'est pas plus compliqué :
SQLExecDirect(stmt, "SELECT * FROM pays_tbl;", SQL_NTS);
Que l'on aurait également pu faire en deux temps (mais on va garder la première) :
SQLPrepare(stmt, "SELECT * FROM pays_tbl;", SQL_NTS);
SQLExecute(stmt);
Il ne nous reste donc plus qu'à parcourir les enregistrements retournés et ensuite les afficher.
La fonction qui nous permettra d'y arriver est SQLFetch, qu'on utilisera en boucle.
SQLRETURN SQLFetch(SQLHSTMT StatementHandle);
Contrairement à SQLFetch qui ne sait qu'avancer d'un pas (c'est-à-dire aller à l'enregistrement
suivant), la fonction SQLFetchScroll permet de spécifier une direction et un pas.
SQLRETURN SQLFetchScroll(SQLHSTMT StatementHandle, SQLSMALLINT
FetchOrientation, SQLINTEGER FetchOffset);
Avec les différentes directions possibles : SQL_FETCH_NEXT, SQL_FETCH_PRIOR,
SQL_FETCH_FIRST, SQL_FETCH_LAST, SQL_FETCH_ABSOLUTE,
SQL_FETCH_RELATIVE et SQL_FETCH_BOOKMARK. La fonction
SQLExtendedFetch est identique à SQLFetchScroll mais fournit plus d'infos sur le résultat
de l'opération.
Comme nous le savons très bien une ligne (un enregistrement) est organisée en colonnes
(numérotées de 1 à n). La fonction SQLGetData permet de récupérer le contenu d'une
colonne de la ligne courante dans le jeu d'enregistrements identifié par StatementHandle.
SQLRETURN SQLGetData( SQLHSTMT StatementHandle, SQLUSMALLINT
ColumnNumber, SQLSMALLINT TargetType,
SQLPOINTER TargetValuePtr, SQLINTEGER
BufferLength, SQLINTEGER * IndicatorPtr );
Comme nous pouvons le constater, la fonction a besoin de connaître le type de la variable
destinée à contenir la donnée qu'on veut récupérer. Quel que soit le type de la donnée dans la
base, le pilote ODBC fera toujours les conversions nécessaires suivant la valeur de l'argument
TargetType (évidemment il faut quand même que la conversion ait un sens ...). Le type
SQL_C_CHAR (chaîne de caractères C) s'avère donc particulièrement intéressant dans la
mesure où il est possible de formater n'importe quelle donnée en chaîne de caractères et qu'il
est ensuite assez facile d'analyser une chaîne, de la convertir en entier ou en flottant ou encore
d'en extraire les informations utiles. Cependant, rien ne nous empêche d'utiliser directement
les types SQL_C_BIT (caractère), SQL_C_SLONG, SQL_C_ULONG, SQL_C_DOUBLE,
SQL_C_BINARY (données au format brut), etc.
Le dernier argument de cette fonction permet de spécifier l'adresse d'une variable destinée à
contenir des informations supplémentaires sur la donnée retournée (longueur si c'est une
chaîne, SQL_NULL_DATA si la colonne est vide, etc.). Et voilà, on a pratiquement terminé.
Avouez que c'a n'a rien de compliqué, mais on va tout de même faire un petit exemple en
guise de résumé.
Fichier : pays_dsn.c
#include
#include
#include
#include
<stdio.h>
<windows.h>
<sql.h>
<sqlext.h>
int main()
{
SQLHENV env;
if ( !SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, NULL, &env))
)
fprintf(stderr, "La fonction SQLAllocHandle a echoue
(SQL_HANDLE_ENV).\n");
else
{
if ( !SQL_SUCCEEDED(SQLSetEnvAttr(env,
SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0)) )
fprintf(stderr, "La fonction SQLSetEnvAttr a
echoue.\n");
else
{
SQLHDBC con;
if ( !SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_DBC, env,
&con)) )
fprintf(stderr, "La fonction SQLAllocHandle a echoue
(SQL_HANDLE_DBC).\n");
else
{
SQLCHAR dsn[] = "pays_dsn", uid[] = "melem", pwd[] =
"1234";
if ( !SQL_SUCCEEDED(SQLConnect(con, dsn, SQL_NTS,
uid, SQL_NTS, pwd, SQL_NTS)) )
fprintf(stderr, "La fonction SQLConnect a
echoue.\n");
else
{
SQLHSTMT stmt;
if (
!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_STMT, con, &stmt)) )
fprintf(stderr, "La fonction SQLAllocHandle
a echoue (SQL_HANDLE_STMT).\n");
else
{
if ( !SQL_SUCCEEDED(SQLExecDirect(stmt,
"SELECT * FROM pays_tbl;", SQL_NTS)) )
fprintf(stderr, "La fonction
SQLExecDirect a echoue.\n");
else
{
SQLCHAR pays[15], capitale[15];
printf("PAYS > CAPITALE\n\n");
while (SQL_SUCCEEDED(SQLFetch(stmt)))
{
SQLGetData(stmt, 1, SQL_C_CHAR,
pays, sizeof(pays), NULL);
SQLGetData(stmt, 2, SQL_C_CHAR,
capitale, sizeof(capitale), NULL);
printf("%-14s %-14s\n", pays,
capitale);
}
}
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
}
SQLDisconnect(con);
}
SQLFreeHandle(SQL_HANDLE_DBC, con);
}
}
SQLFreeHandle(SQL_HANDLE_ENV, env);
}
return 0;
}
IV-C. Amélioration
Le programme précédent était trop dépendant de la structure actuelle de la table pays_tbl. En
effet, il y a une fonction très utile qu'on n'a pas encore étudiée : SQLNumResultCols.
SQLRETURN SQLNumResultCols(SQLHSTMT StatementHandle, SQLSMALLINT *
ColumnCountPtr);
Comme son nom l'indique, cette fonction permet de récupérer le nombre de colonnes dans le
jeu d'enregistrement retourné par une commande ou une requête. Cela permet par exemple de
traiter le résultat d'une requête SELECT sans avoir à connaître d'avance la structure de ce
résultat, comme le montre l'extrait de code suivant :
if ( !SQL_SUCCEEDED(SQLExecDirect(stmt, "SELECT * FROM pays_tbl;",
SQL_NTS)) )
fprintf(stderr, "La fonction SQLExecDirect a echoue.\n");
else
{
SQLSMALLINT nbcolumns;
SQLCHAR (*columns) [15];
SQLNumResultCols(stmt, &nbcolumns);
columns = malloc(nbcolumns * sizeof(columns[0]));
if (columns != NULL)
{
printf("TABLE : PAYS_TBL\n\n");
while (SQL_SUCCEEDED(SQLFetch(stmt)))
{
SQLSMALLINT i;
for(i = 0; i < nbcolumns; i++)
{
SQLGetData(stmt, i + 1, SQL_C_CHAR, columns[i],
sizeof(columns[i]), NULL);
printf("%-14s ", columns[i]);
}
putchar('\n');
}
free(columns);
}
}
IV-D. Une autre méthode : attacher les colonnes
Cette méthode permet d'éviter d'effectuer un appel répété à SQLGetData pour lire le contenu
d'un enregistrement en attachant préalablement les colonnes du tableau à différentes variables
avant d'entrer dans une boucle (alors que SQLGetData était appelée à chaque itération), en
utilisant la fonction SQLBindCol comme le montre l'exemple suivant :
if ( !SQL_SUCCEEDED(SQLExecDirect(stmt, "SELECT * FROM pays_tbl;",
SQL_NTS)) )
fprintf(stderr, "La fonction SQLExecDirect a echoue.\n");
else
{
SQLSMALLINT nbcolumns;
SQLCHAR (*columns) [15];
SQLNumResultCols(stmt, &nbcolumns);
columns = malloc(nbcolumns * sizeof(columns[0]));
if (columns != NULL)
{
SQLSMALLINT i;
/* Attacher les colonnes */
for(i = 0; i < nbcolumns; i++)
SQLBindCol(stmt, i + 1, SQL_C_CHAR, columns[i],
sizeof(columns[i]), NULL);
printf("TABLE : PAYS_TBL\n\n");
while (SQL_SUCCEEDED(SQLFetch(stmt)))
{
for(i = 0; i < nbcolumns; i++)
printf("%-14s ", columns[i]);
putchar('\n');
}
free(columns);
}
}
V. Analyse des résultats
Les résultats retournés par une commande se présentent souvent sous la forme d'un jeu
d'enregistrements, chaque enregistrement étant composé de un ou plusieurs champs
(colonnes). Pour bien exploiter ces résultats, il faut connaître les attributs de chaque champ
(son nom, le type de la donnée qu'elle contient, sa longueur max, etc.). Nous avons déjà vu la
fonction SQLNumResultCols qui permet de connaître le nombre de colonnes dans un jeu
d'enregistrements donné. La fonction SQLColAttribute permet de lire un attribut d'une
colonne dont le numéro est spécifié en argument.
SQLRETURN SQLColAttribute( SQLHSTMT StatementHandle,
SQLUSMALLINT ColumnNumber, SQLUSMALLINT
FieldIdentifier,
SQLPOINTER CharacterAttributePtr,
SQLSMALLINT BufferLength,
SQLSMALLINT * StringLengthPtr,
SQLPOINTER NumericAttributePtr );
StatementHandle identifie le jeu d'enregistrements dans lequel on souhaite travailler,
ColumnNumber le numéro de la colonne qui nous intéresse et FieldIdentifier l'attribut qu'on
veut lire.
Quand cet attribut est une chaîne de caractères (par exemple le nom de la colonne), alors il est
retourné dans le tampon pointé par CharacterAttributePtr. BufferLength doit alors indiquer la
taille de ce tampon. StringLengthPtr quant à lui permet de spécifier un pointeur vers un
tampon dans lequel sera retourné la longueur de la chaîne, sans compter le caractère de fin de
chaîne. Si elle est supérieure ou égale à BufferLength, alors seuls les BufferLength - 1
premiers caractères de la chaîne ont été copiés dans le tampon pointé par
CharacterAttributePtr, le dernier caractère étant toujours le caractère de fin de chaîne.
Quand cet attribut est un nombre, il est copié dans le tampon pointé par NumericAttributePtr.
CharacterAttributePtr et NumericAttributePtr ne sont jamais utilisés en même temps.
Voici les champs qui nous intéressent le plus souvent :



SQL_DESC_BASE_COLUMN_NAME : nom de la colonne.
SQL_DESC_LABEL : label de la colonne (peut être un titre, un alias ou le nom de la
colonne par exemple).
SQL_DESC_NULLABLE : un entier qui indique si oui (SQL_NULLABLE) ou non
(SQL_NO_NULLS) la colonne accepte NULL (SQL). Peut être
SQL_NULLABLE_UNKNOWN.

SQL_DESC_LENGTH : longueur max (sans compter le '\0' quand c'est une chaîne)
du champ lorsque ce champ contient une chaîne (de taille fixe ou de taille variable) ou
des données brutes.
SQL_DESC_OCTET_LENGTH : longueur max du champ lorsque ce champ
contient une chaîne (de taille fixe ou de taille variable) ou des données brutes.
SQL_DESC_TYPE : un entier qui indique le type de la donnée que contient la
colonne.
SQL_DESC_TYPE_NAME : une chaîne qui indique le type de la donnée que
contient la colonne.



Nous sommes donc à présent en mesure d'écrire une fonction simple permettant d'exécuter et
d'obtenir sur la sortie standard le résultat d'une commande. La fonction suivante est loin de la
perfection mais elle nous servira quand même assez dans ce tutoriel.
SQLRETURN sql_do_cmd(SQLHSTMT stmt, SQLCHAR * query)
{
SQLRETURN ret = SQLExecDirect(stmt, query, SQL_NTS);
if ( !SQL_SUCCEEDED(ret) )
fprintf(stderr, "SQL ERROR : %s\n", query);
else
sql_show_results(stmt);
return ret;
}
Avec la fonction sql_show_results définie comme suit :
void sql_show_results(SQLHSTMT stmt)
{
SQLSMALLINT nbcolumns;
SQLCHAR (*columns) [15];
SQLNumResultCols(stmt, &nbcolumns);
if (nbcolumns > 0)
{
columns = calloc(nbcolumns, sizeof(columns[0])); /* Il faut
utiliser calloc */
/* car chaque champ doit etre initialise afin d'eviter
d'afficher de valeurs */
/* non desirees lorsque le champ est cense etre vide.
*/
if (columns != NULL)
{
SQLSMALLINT i;
for(i = 0; i < nbcolumns; i++)
SQLBindCol(stmt, i + 1, SQL_C_CHAR, columns[i],
sizeof(columns[i]), NULL);
/* Afficher l'en-tete */
putchar('+');
for(i = 0; i < nbcolumns; i++)
printf("--------------+");
putchar('\n');
putchar('|');
for(i = 0; i < nbcolumns; i++)
{
SQLCHAR column_name[15] = "";
SQLColAttribute(stmt, i + 1, SQL_COLUMN_NAME,
column_name, sizeof(column_name), NULL, NULL);
printf("%-14s|", column_name);
}
putchar('\n');
putchar('+');
for(i = 0; i < nbcolumns; i++)
printf("--------------+");
putchar('\n');
/* Afficher les enregistrements */
while (SQL_SUCCEEDED(SQLFetch(stmt)))
{
putchar('|');
for(i = 0; i < nbcolumns; i++)
printf("%-14s|", columns[i]);
putchar('\n');
putchar('+');
for(i = 0; i < nbcolumns; i++)
printf("--------------+");
putchar('\n');
/* Avant de tenter de lire l'enregistrement suivant,
initialiser les champs */
for(i = 0; i < nbcolumns; i++)
columns[i][0] = '\0';
}
/* Termine */
free(columns);
}
}
}
Il y a aussi la fonction SQLDecribeCol qui permet d'obtenir en un seul appel le nom d'une
colonne, le type de la donnée contenue dans cette colonne ainsi que d'autres informations qui
peuvent être utiles. Lorsque l'information que l'on désire connaître ne peut pas être obtenue
par SQLDescribeCol, on continuera à utiliser SQLColAttribute.
SQLRETURN SQLDescribeCol( SQLHSTMT StatementHandle, SQLSMALLINT
ColumnNumber,
SQLCHAR * ColumnName, SQLSMALLINT
BufferLength, SQLSMALLINT * NameLengthPtr,
SQLSMALLINT * DataTypePtr, SQLUINTEGER *
ColumnSizePtr,
SQLSMALLINT * DecimalDigitsPtr,
SQLSMALLINT * NullablePtr );
VI. Les diagnostics
Nous avons déjà vu la macro SQL_SUCCEEDED qui retourne VRAI ou FAUX selon qu'on
lui a passé une valeur indiquant un succès ou un échec. Plus précisément SQL_SUCCEEDED
retourne VRAI si l'argument vaut SQL_SUCCESS ou SQL_SUCCESS_WITH_INFO et
FAUX dans le cas contraire. Si nous n'avons pas expliqué plutôt ce que signifiaient ces codes,
ce n'est pas par hasard ...
Selon l'issue d'un appel, le driver reporte parfois un certain nombre de diagnostics permettant
d'avoir des informations supplémentaires sur le déroulement de l'appel. A noter que le
gestionnaire de pilotes lui-même est également susceptible de reporter des diagnostics. Ces
diagnostics se présentent sous la forme d'un jeu d'enregistrements. Quand une fonction
retourne SQL_SUCCESS, cela signifie qu'elle a réussi et qu'en général, aucun diagnostic n'a
été reporté par le driver ou le gestionnaire lui-même. Quand une fonction retourne
SQL_ERROR, cela signifie qu'elle a échoué et que le le driver ou le gestionnaire de pilotes
ou les deux en même temps ont reporté des diagnostics décrivant les différentes causes de
l'erreur. Quand une fonction retourne SQL_SUCCESS_WITH_INFO, cela signifie qu'elle a
réussi mais le pilote ou le gestionnaire de pilotes ou les deux en même temps ont reporté des
diagnostics fournissant plus de détails sur la réussite de l'appel. Quand une fonction retourne
valeur qui n'est ni SQL_SUCCESS, ni SQL_SUCCESS_WITH_INFO, ni SQL_ERROR, cela
signifie que la fonction a échoué mais qu'aucun diagnostic n'a été reporté. Il y a toutefois
quelques exceptions à cette règle.
Un diagnostic est composé de plusieurs champs. Le nombre exact de champs par diagnostic
dépend de la version d'ODBC utilisée. D'ailleurs, un pilote peut ajouter des champs
supplémentaires mais ces champs ne sont donc, évidemment, pas standards. De plus, la
première ligne (numérotée 0) du jeu de diagnostics retourné par un appel est appelée en-tête
et possède une structure différente de celle des autres diagnostics qui sont appelés les
enregistrements. En d'autres termes, les champs qui constituent l'en-tête sont différents des
champs qui constituent un enregistrement. L'en-tête est rarement consulté, mais cela ne
signifie pas qu'il est n'a aucune utilité.
La fonction SQLGetDiagField permet de lire la valeur d'un champ d'un diagnostic donné. Il
n'existe pas de fonction permettant de connaître le nombre d'enregistrements dans le jeu de
diagnostics retourné mais cette information peut être lue dans le champ
SQL_DIAG_NUMBER de l'en-tête. Dans la pratique, on n'a pas vraiment besoin de ce
champ puisqu'il suffit d'utiliser cette fonction à l'intérieur d'une boucle, jusqu'à ce qu'elle
retourne SQL_NO_DATA, pour parcourir tous les enregistrements.
SQLRETURN SQLGetDiagField( SQLSMALLINT
SQLSMALLINT
SQLSMALLINT
DiagInfoPtr, SQLSMALLINT BufferLength,
SQLSMALLINT
HandleType, SQLHANDLE Handle,
RecNumber,
DiagIdentifier, SQLPOINTER
* StringLengthPtr );
HandleType doit indiquer le type du handle fourni en deuxième argument de la fonction. Par
exemple, si SQLConnect retourne SQL_ERROR et qu'on veut connaître la ou les raisons de
cet échec, on effectuera un ou plusieurs appels à SQLGetDiagRec en passant
SQL_HANDLE_DBC dans l'argument HandleType et le handle de la connexion dans
l'argument Handle.
RecNumber indique le numéro de la ligne qu'on veut lire (0 pour l'en-tête, 1 pour le premier
enregistrement), DiagIdentifier le champ qui nous intéresse, DiagInfoPtr un pointeur vers le
tampon destiné à recevoir l'information lue et BufferLength la taille de ce tampon. Et enfin,
l'argument optionnel StringLengthPtr n'est utilisé que lorsque l'information demandée est une
chaîne de caractères. Dans ce cas, il permet de passer un pointeur vers un tampon destiné à
recevoir la longueur de cette chaîne (caractère de fin de chaîne exclu). Si elle est supérieure
ou égale à BufferLength, alors seuls les BufferLength - 1 premiers caractères de la chaîne ont
été copiés dans le tampon pointé par DiagInfoPtr, le dernier caractère étant toujours le
caractère de fin de chaîne.
Parmi les champs constituant chaque enregistrement, SQL_DIAG_SQLSTATE et
SQL_DIAG_MESSAGE_TEXT sont des plus importants car ils fournissent respectivement
son code ainsi que sa description. Le champ SQL_DIAG_NATIVE a également son
importance puisqu'il s'agit, un peu à la manière de SQLSTATE, d'un code d'erreur, sauf que
sa signification est dépendante du driver. Un SQLSTATE est une chaîne composée de 5
(SQL_SQLSTATE_SIZE) caractères (6 lorsqu'on compte le caractère de fin de ligne) servant
à déterminer globalement la cause d'une erreur, la description détaillée étant fournie par le
MESSAGE_TEXT. La taille d'un tel message, en incluant le caractère de fin de chaîne, est
garanti être tout au plus égale à SQL_MAX_MESSAGE_LENGTH.
Voici une fonction qui permet d'afficher les différents diagnostics retournés par un appel
lorsqu'il y en a :
void sql_perror(SQLSMALLINT HandleType, SQLHANDLE Handle, const char
* title)
{
SQLCHAR s[SQL_MAX_MESSAGE_LENGTH];
SQLSMALLINT ligne = 1;
fprintf(stderr, "%s :\n", title);
while (SQLGetDiagField(HandleType, Handle, ligne,
SQL_DIAG_MESSAGE_TEXT, s, sizeof(s), NULL) != SQL_NO_DATA)
{
fprintf(stderr, "%d. %s\n", ligne, s);
ligne++;
}
}
Etant donné que les champs SQLSTATE, NATIVE et MESSAGE_TEXT sont les plus
utilisés lorsqu'on lit un diagnostic, ODBC fournit la fonction SQLGetDiagRec qui s'utilise à
peu près de la même manière que SQLGetDiagField, à la différence qu'elle retourne
directement ces 3 champs alors qu'on aurait effectué 3 appels pour les avoir si on avait utilisé
SQLGetDiagField. Par contre, SQLGetDiagRec ne peut lire qu'un enregistrement (ce qui
signifie qu'il faut passer un nombre supérieur ou égal à 1 dans l'argument RecNumber). Pour
lire l'en-tête, il faut utiliser la fonction SQLGetDiagField.
SQLRETURN SQLGetDiagRec( SQLSMALLINT HandleType, SQLHANDLE Handle,
SQLSMALLINT RecNumber,
SQLCHAR * SqlState, SQLINTEGER *
NativeErrorPtr,
SQLCHAR * MessageText, SQLSMALLINT
BufferLength,
SQLSMALLINT * TextLengthPtr );
VII. Se connecter de différentes manières
VII-A. La fonction SQLDriverConnect
La fonction SQLConnect permet de se connecter à une base de données en spécifiant le nom
de la source de données correspondante, le login et le mot de passe, les informations relatives
à la base elle-même étant stockée dans la source de données. Cependant, il peut arriver parfois
que l'on souhaite spécifier plus de paramètres de connexion (qui peuvent être spécifiques du
SGBD) et dans ce cas on utilisera la fonction SQLDriverConnect.
SQLRETURN SQLDriverConnect( SQLHDBC ConnectionHandle,
SQLHWND WindowHandle,
SQLCHAR * InConnectionString,
SQLSMALLINT StringLength1,
SQLCHAR * OutConnectionString,
SQLSMALLINT BufferLength,
SQLSMALLINT * StringLength2Ptr,
SQLUSMALLINT DriverCompletion );
C'est une fonction très souple, permettant de réaliser de grandes choses. Un des paramètres
importants de cette fonction est le paramètre DriverCompletion. Ce paramètre permet de
spécifier la manière d'établir la connexion. Nous y reviendrons assez souvent mais parlons
tout d'abord des autres paramètres.
VII-B. Les paramètres
ConnexionHandle est bien sûr le handle de connexion à associer à la connexion.
WindowHandle n'a de sens que si le gestionnaire de pilotes ODBC a été sommé d'afficher une
boîte de dialogue permettant à l'utilisateur de fournir lui-même les paramètres de connexion
(source de données, login, mot de passe, etc.), sinon on mettra tout simplement NULL. Quand
il est utilisé, WindowHandle permet de spécifier quelle fenêtre sera le parent de cette boîte de
dialogue. Dans une application console, on pourra par exemple spécifier le handle de la
fenêtre console que l'on peut récupérer à l'aide de la fonction GetConsoleWindow depuis
Windows 2000 ou alors le handle du bureau que l'on peut récupérer à l'aide de
GetDesktopWindow. Sous UNIX, n'importe quelle valeur différente de 0 permet de donner
au driver l'autorisation d'afficher la boîte de dialogue.
InConnectionString est une chaîne contenant les paramètres de connexion séparés par un
point-virgule, chaque nom de paramètre étant suivi du signe = et de la valeur du paramètre,
par exemple : "DSN=pays_dsn;UID=melem;PWD=1234". Comme on peut le voir d'après cet
exemple, la dernière paire paramètre-valeur n'a plus besoin d'être terminée par un pointvirgule. Certains paramètres (comme DSN, UID et PWD par exemple) sont standard, d'autres
sont spécifiques au SGBD. StringLength1 doit bien sûr spécifier la longueur de la chaîne
InConnectionString mais comme d'habitude on peut utiliser SQL_NTS.
En fait, deux cas peuvent se présenter :

Les paramètres de connexion sont fournis par le programme, dans ce cas
InConnexionString et StringLength1 trouvent pleinement leur utilité.

Les paramètres de connexion sont à récupérer depuis une boîte de dialogue, dans ce
cas on peut mettre NULL ou une chaîne vide dans InConnexionString et 0 dans
StringLength1. Si on spécifie quand même un ou plusieurs paramètres, les champs de
la boîte de dialogue, selon le paramètre DriverCompletion, seront initialisés avec ces
valeurs sinon elles seront tout simplement ignorées.
A noter que, à cause de sa syntaxe, certains caractères doivent s'utiliser avec précaution. Le
cacartère ; par exemple ne peut pas apparaître n'importe où ! Ainsi, si la valeur d'un paramètre
(ou dans certains cas le nom d'un paramètre) comporte un ou plusieurs caractères parmi [ ] { }
( ) , ; ? * = ! @, il est conseillé de la mettre entre accolades (PARAM={value};) afin d'éviter
toute mauvaise interprétation.
Une fois toutes les informations nécessaires recueillies, le driver va initier la connexion puis
retournera via les paramètres facultatifs OutConnectionString et StringLength2Ptr les
informations sur la chaîne de connexion qui fut utilisée. Si le paramètre OutConnectionString
est utilisé, BufferLength doit spécifier le nombre de caractères maximum qu'on peut placer
dans le buffer.
Et enfin, comme nous l'avons déjà dit dès le départ, le paramètre DriverCompletion permet
d'indiquer à la fonction certaines directives pour l'aider à établir la connexion. Lorsque ces
directives parviennent au driver (ce qui signifie que le pilote à utilisé a été spécifié dans la
chaîne de connexion) :

SQL_DRIVER_PROMPT affiche inconditionellement une boîte de dialogue pour
entrer les paramètres de connexion. Les champs correspondant à des paramètres déjà
connus seront initialisés.

SQL_DRIVER_COMPLETE ou SQL_DRIVER_COMPLETE_REQUIRED
permettent toutes d'établir la connexion à partir de la chaîne InConnexionString mais
si et seulement si les paramètres fournis dans cette chaîne sont incomplets (c'est-à-dire
qu'un ou plusieurs paramètres indispensables n'ont pas été fournis) ou invalides, alors
une boîte de dialogue avec les champs éventuellement initialisés par les valeurs des
paramètres déjà fournis va être affichée pour compléter le informations manquantes
sauf que si on a utilisé SQL_DRIVER_COMPLETE_REQUIRED, alors les champs
déjà fournis ne pourront plus être modifiés alors que dans l'autre cas
(SQL_DRIVER_COMPLETE) l'utilisateur pourra modifier même les paramètres qui
sont déjà bons.

SQL_DRIVER_NOPROMPT spécifie que la connexion doit se faire en utilisant la
chaîne passée via le paramètre InConnexionString. Si la chaîne est mauvaise, la
fonction échouera.
Si la chaîne de connexion ne spécifie aucun pilote (discuté dans le paragraphe suivant) et
qu'une boîte de dialogue doit être affichée, alors c'est le gestionnaire de pilotes qui se chargera
du travail.
Si une boîte de dialogue s'affiche et que l'utilisateur annule celle-ci, SQLDriverConnect
retournera SQL_NO_DATA.
VII-C. La chaîne de connexion
Les la liste complète des paramètres qu'on peut spécifier pour se connecter à une base de
données dépend du SGBD. Néanmoins certains paramètres comme DSN, UID et PWD sont
standard (ce sont les paramètres qu'on devait passer à la fonction SQLConnect). Mais il y a
également deux autres paramètres qu'on peut utiliser à la place de DSN (il ne faut surtout pas
les utiliser en même temps !) que nous n'avons pas encore étudiés : DRIVER et FILEDNS.
DRIVER permet de se connecter au serveur lui-même en spécifiant le nom (tel qu'il apparaît
dans l'administrateur de sources de données) du pilote à utiliser. Si vous avez bien compris, ce
paramètre permet de se connecter à un SGBD (ou, en spécifiant plus de paramètres,
directement à une base de données) sans passer par une source de données ! On peut obtenir la
liste de tous les pilotes installés à l'aide de la fonction SQLDrivers. On peut également
observer cette liste depuis l'administrateur de sources de données ODBC. Hélas, cela ne nous
renseigne pas toujours sur tous les paramètres. Ces paramètres, on les trouve normalement
dans la documentation du pilote mais aussi ... dans une source de données fichier ! En effet,
une source de données fichier est un bête fichier texte qui contient les paramètres nécessaires
(et en plus, pas seulement ce qui sont vraiment nécessaires ...) pour la connexion. Quant à
FILEDSN, il est utilisé pour se connecter à une base de données en passant par une source de
données fichier. C'est la méthode la moins conseillée.
Voici donc, à titre illustratif, un programme qui se connecte à MySQL par l'intermédiaire du
pilote MySQL ODBC 3.51 Driver et qui, une fois connecté, affiche la liste des bases de
données actuellement sur le serveur. Dans cet exemple, nous supposons que le serveur se
trouve sur la machine locale (localhost).
#include
#include
#include
#include
<stdio.h>
<windows.h>
<sql.h>
<sqlext.h>
void sql_perror(SQLSMALLINT HandleType, SQLHANDLE Handle, const char *
title);
SQLRETURN sql_do_cmd(SQLHSTMT stmt, SQLCHAR * query);
int main()
{
SQLHENV env;
if ( !SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, NULL, &env)) )
fprintf(stderr, "SQLAllocHandle (SQL_HANDLE_ENV) : la fonction
a echoue.\n");
else
{
if ( !SQL_SUCCEEDED(SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION,
(void *)SQL_OV_ODBC3, 0)) )
sql_perror(SQL_HANDLE_ENV, env, "SQLSetEnvAttr");
else
{
SQLHDBC con;
if ( !SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_DBC, env,
&con)) )
sql_perror(SQL_HANDLE_ENV, env, "SQLAllocHandle
(SQL_HANDLE_DBC)");
else
{
SQLCHAR in[] = "DRIVER=MySQL ODBC 3.51
Driver;UID=melem;PWD=1234";
if ( !SQL_SUCCEEDED(SQLDriverConnect(con, NULL, in,
SQL_NTS, NULL, 0, NULL,
SQL_DRIVER_NOPROMPT)) )
sql_perror(SQL_HANDLE_DBC, con,
"SQLDriverConnect");
else
{
SQLHSTMT stmt;
if (
!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_STMT, con, &stmt)) )
sql_perror(SQL_HANDLE_DBC, con,
"SQLAllocHandle (SQL_HANDLE_STMT)");
else
{
/*
**********************************************************************
*/
/* [MySQL] La commande SHOW DATABASES affiche
toutes les bases de donnees */
/*
**********************************************************************
*/
sql_do_cmd(stmt, "SHOW DATABASES;");
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
}
SQLDisconnect(con);
}
SQLFreeHandle(SQL_HANDLE_DBC, con);
}
}
SQLFreeHandle(SQL_HANDLE_ENV, env);
}
return 0;
}
VIII. Connaître la structure d'une base de
données
VIII-A. La fonction SQLTables
Cette fonction permet entre autres d'énumérer les tables (tables et vues confondues ...)
contenues dans une base de données. Les informations retournées par cette fonction se
présentent sous la forme d'un jeu d'enregistrements (un enregistrement par table) composé
chacun d'exactement champs à savoir :





TABLE_CAT (champ n° 1) : nom du catalogue auquel appartient la table. Peut être
vide.
TABLE_SCHEM (champ n° 2) : lorsqu'un schéma a été défini, nom du schéma
auquel appartient la table, sinon contient une chaîne vide.
TABLE_NAME (champ n° 3) : nom de la table.
TABLE_TYPE (champ n° 4) : le type de la table (par exemple TABLE indique que
c'est une table, VIEW indique que c'est une vue, et ainsi de suite).
Et enfin REMARKS (champ n° 5) : commentaires ou simplement une chaîne vide.
La fonction SQLTables est déclarée ainsi :
SQLRETURN SQLTables( SQLHSTMT StatementHandle,
SQLCHAR * CatalogName, SQLSMALLINT NameLength1,
SQLCHAR * SchemaName, SQLSMALLINT NameLength2,
SQLCHAR * TableName, SQLSMALLINT NameLength3,
SQLCHAR * TableType, SQLSMALLINT NameLength4 );
StatementHandle est le handle qu'on veut utiliser pour récupérer les résultats. Les tables qui
pourraient être retournées par SQLTables sont uniquement celles qui appartiennent à la base
de données dans laquelle a été allouée StatementHandle. CatalogName, SchemaName,
TableName et TableType sont des paramètres optionnels qui permettent de limiter le résultat
uniquement aux tables répondant à certains critères. Ces paramètres sont par défaut sensibles
à la casse mais ce comportement (ainsi que d'autres propriétés) peut être modifié en affectant
la valeur SQL_TRUE à l'attribut SQL_ATTR_METADATA_ID, ce qui peut se faire à l'aide
de la fonction SQLSetStmtAttr. Ils peuvent également contenir des caractères jokers. Les
seuls jokers standard sont cependant _ (underscore) qui représente exactement un caractère et
% qui représente 0 ou plusieurs caractères. Un pilote donné peut supporter d'autres caractères
spéciaux. Le caractère d'échappement est \ (ainsi, \% par exemple sera interprété comme le
caractère % et non le caractère backslash suivi du joker %). NULL est autorisé pour indiquer
qu'il n'y pas de critère spécifique sur la valeur d'un paramètre. Quant aux paramètres
NameLengthI, ils indiquent bien sûr la longueur de la chaîne passée dans le paramètre
correspondant ou simplement SQL_NTS lorsque la chaîne est déjà terminée par un zéro.
La fonction suivante permet de lister toutes les tables (et vues ..) d'une base de données.
SQLRETURN sql_tables(SQLHSTMT stmt)
{
SQLRETURN ret = SQLTables(stmt, NULL, 0, NULL, 0, NULL, 0, NULL,
0);
if ( !SQL_SUCCEEDED(ret) )
sql_perror(SQL_HANDLE_STMT, stmt, "SQLTables");
else
sql_show_results(stmt);
return ret;
}
Voici d'autres exemples d'appels à SQLTables qui vous aideront peut-être à mieux
comprendre le fonctionnement de cette fonction :
SQLTables(stmt, NULL,
/* Affiche toutes les
*/
SQLTables(stmt, NULL,
/* Affiche toutes les
*/
SQLTables(stmt, NULL,
/* Affiche toutes les
autres ...) */
SQLTables(stmt, NULL,
/* Affiche toutes les
*/
SQLTables(stmt, NULL,
/* Affiche les tables
*/
SQLTables(stmt, NULL,
/* Affiche les tables
*/
0, NULL, 0, NULL, 0, NULL, 0);
tables
0, NULL, 0, "%", SQL_NTS, NULL, 0);
tables
0, NULL, 0, "%", SQL_NTS, "TABLE", SQL_NTS);
tables (TABLEs uniquement, pas les vues et
0, NULL, 0, "%", SQL_NTS, "VIEW", SQL_NTS);
vues
0, NULL, 0, "%tbl", SQL_NTS, NULL, 0);
dont le nom se termine par tbl
0, "", SQL_NTS, "%", SQL_NTS, NULL, 0);
qui n'appartiennent a aucun schema
Sachez également que certaines formes spéciales d'utilisation de SQLTables existent. Par
exemple, quand CatalogName vaut SQL_ALL_CATALOGS ("%") et que les autres
paramètres vallent NULL ou une chaîne vide, alors SQLTables retourne la liste de tous les
catalogues existants. Quand SQLTables est appelée ainsi, les autres champs du jeu
d'enregistrements retourné contiennent tous une chaîne vide et donc n'ont aucun intérêt.
VIII-B. La fonction SQLColumns
Cette fonction s'utilise de la même manière que SQLTables et permet d'énumérer les colonnes
des différentes tables de la base, avec la possibilité de limiter le résultat à uniquement
certaines colonnes de certaines tables, tout comme dans SQLTables.
SQLRETURN SQLColumns( SQLHSTMT StatementHandle,
SQLCHAR * CatalogName, SQLSMALLINT
NameLength1,
SQLCHAR * SchemaName, SQLSMALLINT NameLength2,
SQLCHAR * TableName, SQLSMALLINT NameLength3,
SQLCHAR * ColumnName, SQLSMALLINT NameLength4
);
Les paramètres ne sont plus à présenter. Seulement, sachez que lorsque CatalogName est
utilisé (c'est-à-dire n'est pas NULL), il ne peut pas contenir de caractère joker (les jokers sont
autorisés dans les autres paramètres).
SQLColumns retourne le résultat sous forme d'un jeu d'enregistrements composé chacun de
18 champs (ou plus selon le driver) dont les plus intéressants, à part TABLE_CAT (champ n°
1), TABLE_SCHEM (champ n° 2) et TABLE_NAME (champ n° 3) que nous connaissons
déjà, sont :







COLUMN_NAME (4) : nom de la colonne.
DATA_TYPE (5) : un entier permettant de connaître le type de la donnée de la
colonne.
TYPE_NAME (6) : une chaîne qui fournit le type de la donnée de la colonne
COLUMN_SIZE (7) : un entier qui indique la taille de la colonne. Sa signification est
dépendante de DATA_TYPE.
NULLABLE (11) : un entier permettant de savoir si le champ accepte la valeur
NULL (SQL).
REMARKS (12) : commentaires.
Et IS_NULLABLE (18) : une chaîne qui indique si oui ou non la colonne accepte la
valeur NULL (SQL).
IX. Conclusion
En résumé, nous avons appris à créer une source de données, à se connecter à un serveur ou
directement à une base de données (avec ou sans source de données), à exécuter une requête
et à récupérer les résultats. Bien entendu, cela ne signifie pas que les possibilités d'ODBC
s'arrêtent là, bien au contraire et c'est pourquoi nous n'avons étudié ici que les bases mais au
moins, vous êtes maintenant prêt aborder les fonctionnalités avancées (procédures stockées,
batchs SQL, requêtes paramétrées, transactions, curseurs, extensions du langage SQL, etc.) de
cette API.
Téléchargement