Nous arrivons maintenant à la partie "théorique" du tuto, dans cette partie nous allons étudier les principales fonctions de l'API MySQL pour pouvoir ensuite créer un petit jeu utilisant tout ce qu'on aura appris.
Connexion/déconnexion
Tout d'abord créez un nouveau projet en console en prenant bien soin de linker ce qu'il faut et de mettre les .dll là où il faut.
Une fois ceci prêt vous devez avoir ce code sous Code::Blocks:
Code : C | #include <stdio.h>
#include <stdlib.h>
int main()
{
return 0;
}
|
Mais ça ne suffit pas encore pour pouvoir utiliser l'API MySQL, pour cela il faut inclure 2 librairies:
#include <winsock.h>
#include <MYSQL/mysql.h>
Vérifiez que vous avez bien mis #include <winsock.h> avant #include <MYSQL/mysql.h>, car dans le cas contraire vous aurez droit à une belle petite erreur.

Voilà maintenant, vous pouvez compiler et que se passe-t-il? Comment ça, pas grand chose, vous ne vous attendiez quand même pas à ce que votre programme se mette à danser la samba après 2 includes.
lors une fois que tout se passe bien lorsque vous compiler vous pouvez continuer ce tuto. Maintenant nous allons faire quelque chose d'
indispensable pour pouvoir faire des requêtes dans votre programme:
l'initialisation, la connexion et la déconnexion. Eh oui sans ces choses là vous n'iriez pas bien loin , sauf pour la déconnexion mais il est quand même bien préférable de la faire croyez moi.
Tout d'abord il va falloir que nous déclarions un pointeur de type MYSQL, ce pointeur nous servira tout au long du tuto pour pouvoir utiliser la plupart des fonctions.
Donc déclarons notre pointeur comme ceci:
Code : C
Puis initialisons MySQL, la fonction pour faire ça est tout simplement:
mysql_init(MYSQL *mysql);
.
Une fois l'initialisation faite il faut se connecter, pour cela nous allons utiliser 2 fonctions que voici:
Code : C | mysql_options(MYSQL *mysql, enum mysql_option option, const char *arg);
mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag);
|
Prenons-les dans l'ordre:
-mysql_options:
- Le premier argument est le pointeur de structure que nous avons vu juste avant.
- Le 2e argument est l'option que nous voulons configurer, nous utiliserons toujours MYSQL_READ_DEFAULT_GROUP qui lie les options spécifiées dans le fichier my.cnf.
- Enfin le troisième argument est la valeur de cette option, donc vous pouvez mettre le nom que vous voulez.
-mysql_real_connect:
- Le premier argument est toujours le pointeur de structure.
- Le 2e argument est le nom de domaine ou l'adresse de votre hébergeur, dans mon cas c'estwww.goldzoneweb.info.S'il est marqué 'localhost' pour vous je vous conseille de faire comme moi et mettre l'adresse de votre hébergeur car 'localhost' engendre des erreurs.
- Le 3e argument est votre identifiant de connexion.
- Le 4e argument est votre mot de passe.
- Le 5e argument est le nom de votre base de données.
- Le 6e argument est le port, je vous conseille de mettre 0 pour éviter les erreurs.
- Le 7e argument est le socket à utiliser, je vous conseille de mettre NULL ici.
- Et le 8e argument est le flag, je vous conseille de mettre 0.
Certains hébergeurs bloquent le port pour la connexion, il vous est donc impossible d'utiliser l'api avec ces hébergeurs. (information de
Arabesque356)
Voilà pour les arguments de ces 2 fonctions, comme vous l'aurez sûrement compris, mysql_options sert à spécifier des options de connexion et mysql_real_connect sert à se connecter à votre base de données.
Enfin il vous reste à fermer la connexion MySQL, pour cela appelez la fonction
mysql_close(MYSQL *mysql);
.
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | #include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <MYSQL/mysql.h>
int main()
{
MYSQL mysql;
mysql_init(&mysql);
mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"option");
if(mysql_real_connect(&mysql,"www.goldzoneweb.info","mon_pseudo","******","ma_base",0,NULL,0))
{
mysql_close(&mysql);
}
else
{
printf("Une erreur s'est produite lors de la connexion à la BDD!");
}
return 0;
}
|
Voilà je ne crois pas avoir besoin d'expliquer ce code si ce n'est que lorsque la connexion plante
mysql_real_connect
renvoit
NULL
, ce qui nous permet de gérer les erreurs.
Les requêtes n'attendant aucun jeu de résultats
Hein, c'est quoi que ça, un nouveau type de requêtes !?
Non non rassurez-vous. En fait les requêtes n'attendant aucun jeu de résultats sont en fait les requêtes comme
INSERT, DELETE, CREATE, UPDATE
, ...Ces requêtes n'attendent aucune réponse.
Quant aux requêtes qui attendent un jeu de résultats ce sont les requêtes comme:
SELECT, SHOW
, ... car elles attendent une réponse. Dans le cas de
SELECT
elle attend la liste des lignes qui font partie du résultat.
Donc voyons maintenant la fonction permettant de faire des requêtes MySQL:
Code : C | mysql_query(MYSQL *mysql, const char *query);
|
Il vous faudra spécifier pour cette fonction le pointeur de structures ainsi que la requête.
Voici un exemple de son utilisation:
Code : C | mysql_query(&mysql, "INSERT INTO ma_table VALUES('valeur 1', 'valeur 2', 'etc')");
|
Cette requête comme vous pouvez le voir va enregistrer dans la table
ma_table les valeurs que j'ai mises dans l'exemple.
Maintenant si vous voulez savoir combien de lignes ont été affectées par votre requête il suffit d'appeler après la fonction:
mysql_affected_rows(MYSQL *mysql);
.
Voilà ce sont les choses principales qu'il faut savoir pour ce type de requête, maintenant nous pouvons passer aux requêtes de type
SELECT
, ...
Les requêtes attendant un jeu de résultats
Ce type de requête n'est pas plus compliqué que l'autre si ce n'est que comme on attend un résultat il faut faire des opérations en plus.
Pour la requête en elle même ça ne change pas, il faut utiliser la fonction:
mysql_query(MYSQL *mysql, const char *query);
. Mais pour récupérer le résultat il y a 2 façons que nous allons voir ensemble.
mysql_use_result(MYSQL *mysql);
Cette fonction récupère le jeu de résultat mais ne l'enregistre pas dans le client. L'avantage de cette fonction est qu'elle est rapide et qu'elle utilise peu de mémoire, cependant vous ne pourrez pas faire d'opérations comme revenir au résultat n°2, ... avec cette fonction.
Vous devrez stocker la valeur de retour de cette fonction dans un pointeur de structure de type
MYSQL_RES
qui est fait spécialement pour stocker le jeu de résultats.
Mais stocker un jeu de résultat ne suffit pas pour le lire, pour ça vous devrez appeler une autre fonction:
mysql_fetch_row(MYSQL_RES *result);
. La valeur de retour de cette fonction devra quant à elle être stockée dans un pointeur de structure de type
MYSQL_ROW
. Cette fonction stock les valeurs de la ligne suivante dans le jeu de résultats.
Il est nécessaire après avoir utilisé cette fonction de libérer la mémoire allouée, pour cela on utilise la fonction suivante:
Code : C | void mysql_free_result(MYSQL_RES *result);
|
Comme vous le voyez elle prend en paramètre la structure
MYSQL_RES
.
Pensez à vérifier que mysql_use_result
revoie quelque chose, sinon vous aurez le droit à une petite erreur s'il ne renvoie rien.
Voici un exemple de l'utilisation de ces 2 fonctions:
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 | #include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <MYSQL/mysql.h>
int main()
{
//Déclaration du pointeur de structure de type MYSQL
MYSQL mysql;
//Initialisation de MySQL
mysql_init(&mysql);
//Options de connexion
mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"option");
//Si la connexion réussie...
if(mysql_real_connect(&mysql,"www.goldzoneweb.info","mon_pseudo","*****","ma_base",0,NULL,0))
{
//Requête qui sélectionne tout dans ma table scores
mysql_query(&mysql, "SELECT * FROM scores");
//Déclaration des pointeurs de structure
MYSQL_RES *result = NULL;
MYSQL_ROW *row = NULL;
int i = 1;
//On met le jeu de résultat dans le pointeur result
result = mysql_use_result(&mysql);
//Tant qu'il y a encore un résultat ...
while ((row = mysql_fetch_row(result)))
{
printf("Resultat %ld\n", i);
i++;
}
//Libération du jeu de résultat
mysql_free_result(result);
//Fermeture de MySQL
mysql_close(&mysql);
}
else //Sinon ...
{
printf("Une erreur s'est produite lors de la connexion à la BDD!");
}
return 0;
}
|
Et voici ce que ce code me donne:
Code : Console | Resultat 1
Resultat 2
Resultat 3
Resultat 4
Resultat 5
Resultat 6
Press any key to continue. |
Voilà je ne crois pas avoir besoin d'expliquer ce code car tout à été expliqué juste avant: le jeu de résultat est mis dans result, puis on fait une boucle pour lister toutes les lignes.
Mais ça ne me dit pas la valeur de la ligne vous allez me dire, eh bien pour faire ça il nous faut encore 2 fonctions! Eh oui il y en a beaucoup pour une si petite chose.
La première fonction est:
mysql_num_fields(MYSQL_RES *result);
. Cette fonction retourne un entier non signé qui correspond au nombre de champs de la table sélectionnée. Donc pour pouvoir avoir les valeurs de notre requête il va falloir stocker dans une variable le nombre de champs puis faire une boucle pour avoir chaque valeur une par une.
La 2e fonction est:
mysql_fetch_lengths(MYSQL_RES *result);
. Celle-ci retourne un pointeur de long non signé qui correspond à la taille de la valeur courante.
Voici un exemple de requête complet:
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 | #include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <MYSQL/mysql.h>
int main()
{
//Déclaration du pointeur de structure de type MYSQL
MYSQL mysql;
//Initialisation de MySQL
mysql_init(&mysql);
//Options de connexion
mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"option");
//Si la connexion réussie...
if(mysql_real_connect(&mysql,"www.goldzoneweb.info","mon_pseudo","*****","ma_base",0,NULL,0))
{
//Requête qui sélectionne tout dans ma table scores
mysql_query(&mysql, "SELECT * FROM scores");
//Déclaration des pointeurs de structure
MYSQL_RES *result = NULL;
MYSQL_ROW *row = NULL;
unsigned int i = 0;
unsigned int num_champs = 0;
//On met le jeu de résultat dans le pointeur result
result = mysql_use_result(&mysql);
//On récupère le nombre de champs
num_champs = mysql_num_fields(result);
//Tant qu'il y a encore un résultat ...
while ((row = mysql_fetch_row(result)))
{
//On déclare un pointeur long non signé pour y stocker la taille des valeurs
unsigned long *lengths;
//On stocke cette taille dans le pointeur
lengths = mysql_fetch_lengths(result);
//On fait une boucle pour avoir la valeur de chaque champs
for(i = 0; i < num_champs; i++)
{
//On ecrit toutes les valeurs
printf("[%.*s] ", (int) lengths[i], row[i] ? row[i] : "NULL");
}
printf("\n");
}
//Libération du jeu de résultat
mysql_free_result(result);
//Fermeture de MySQL
mysql_close(&mysql);
}
else //Sinon ...
{
printf("Une erreur s'est produite lors de la connexion à la BDD!");
}
return 0;
}
|
Et voici ce que ce code me retourne:
Code : Console | [1] [The BasheR] [130000]
[2] [Rom] [10005120]
[3] [Tib] [51203]
[4] [Joe] [78491]
[5] [Fab] [12054]
[6] [Nic] [1102350] |
La seule chose qui devrait vous poser problème dans le code c'est cette ligne:
Code : C | printf("[%.*s] ", (int) lengths[i], row[i] ? row[i] : "NULL");
|
D'abord, la chaîne de conversion de
printf()
. La chaîne de conversion %.*s est assez austère, il faut l'avouer. Cependant, en prenant les éléments un par un, on arrive à la comprendre sans problème.
Je passerai sur le '%', je crois que ce n'est pas la peine de s'éterniser dessus.
Ensuite vient le point. Il sert à séparer un nombre éventuel indiquant la largeur du champ et la précision désirées. Par exemple, avec %10.20s, on aura une largeur de champ de 10 et on écrira au maximum 20 caractères (spécificateur %s).
Puis, le plus déroutant parce qu'il est rare, l'étoile. Elle désigne en fait la valeur du second argument passé à printf(), qui doit être un int (d'où le cast ). Ainsi, l'appel à
printf()
suivant (tiré du K&R) :
Code : C
... signifiera : "imprimer sur stdout au plus max caractères de la chaîne s".
Je vous épargne l'adaptation à notre propre cas, si vous m'avez suivis jusque là il ne devrait pas y avoir de problème.
Ensuite, la condition ternaire. Elle est un peu ambigüe. Personnellement, je la trouve un peu douteuse. A prendre avec des pincettes.
Voici son équivalent sous forme classique :
Code : C | if (row[i])
printf("%.*s", (int) lengths[i], row[i]);
else
printf("NULL");
|
Or, selon la doc,
mysql_fetch_row()
est censée renvoyer
NULL
en cas d'erreur ou de fin de jeu. C'est ici que se pose le problème. En effet,
NULL
est généralement implémenté ainsi :
Code : C
Auquel cas la condition est parfaitement valable.
En revanche, rien ne garantit (du moins il me semble) que sur certains systèmes,
NULL
ne soit pas égal à une autre valeur que 0. Cela provoquerait un bug important.
Voilà nous avons fini avec la fonction
mysql_use_result
, nous allons donc maintenant passer à l'autre fonction.
mysql_store_result(MYSQL *mysql)
Le principe est le même avec cette fonction, à la différence que celle-ci stocke le jeu de résultat dans une mémoire tampon ce qui rend possible les opérations sur le jeux de résultat mais ça rend la fonction plus lente que l'autre.
Donc pour faire une requête comme celle de l'exemple précédent il suffit juste de changer mysql_use_result par
mysql_store_result
, vous obtiendrez le même résultat mais se sera plus lent:
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 | #include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <MYSQL/mysql.h>
int main()
{
//Déclaration du pointeur de structure de type MYSQL
MYSQL mysql;
//Initialisation de MySQL
mysql_init(&mysql);
//Options de connexion
mysql_options(&mysql,MYSQL_READ_DEFAULT_GROUP,"option");
//Si la connexion réussie...
if(mysql_real_connect(&mysql,"www.goldzoneweb.info","mon_pseudo","*****","ma_base",0,NULL,0))
{
//Requête qui sélectionne tout dans ma table scores
mysql_query(&mysql, "SELECT * FROM scores");
//Déclaration des pointeurs de structure
MYSQL_RES *result = NULL;
MYSQL_ROW *row = NULL;
unsigned int i = 0;
unsigned int num_champs = 0;
//On met le jeu de résultat dans le pointeur result (maintenant on utilise mysql_store_result
result = mysql_store_result(&mysql);
//On récupère le nombre de champs
num_champs = mysql_num_fields(result);
//Tant qu'il y a encore un résultat ...
while ((row = mysql_fetch_row(result)))
{
//On déclare un pointeur long non signé pour y stocker la taille des valeurs
unsigned long *lengths;
//On stocke cette taille dans le pointeur
lengths = mysql_fetch_lengths(result);
//On fait une boucle pour avoir la valeur de chaque champs
for(i = 0; i < num_champs; i++)
{
//On ecrit toutes les valeurs
printf("[%.*s] ", (int) lengths[i], row[i] ? row[i] : "NULL");
}
printf("\n");
}
//Libération du jeu de résultat
mysql_free_result(result);
//Fermeture de MySQL
mysql_close(&mysql);
}
else //Sinon ...
{
printf("Une erreur s'est produite lors de la connexion à la BDD!");
}
return 0;
}
|
Mais alors pourquoi utiliser cette fonction si elle fait la même chose en plus de temps ?!
Eh bien c'est vrai qu'à première vue ça ne sert pas à grand chose, mais en fait l'intérêt de cette fonction est qu'on peut "voyager" dans le jeu de résultat, c'est à dire qu'on peut demander à avoir seulement le résultat n°2, ou n°3, ...
Pour ça il existe une fonction que voici:
mysql_data_seek(MYSQL_RES *result, my_ulonglong offset);
. Cette fonction prend en argument le jeu de résultat ainsi que le numéro de la ligne qu'on veut voir.
Voici un exemple de son utilisation:
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | //Requête qui sélectionne tout dans ma table scores
mysql_query(&mysql, "SELECT * FROM livreor");
//Déclaration des pointeurs de structure
MYSQL_RES *result = NULL;
MYSQL_ROW *row = NULL;
unsigned int i = 0;
unsigned int num_champs = 0;
//On met le jeu de résultat dans le pointeur result
result = mysql_store_result(&mysql);
//On choisie une ligne
mysql_data_seek(result, 4);
//On récupère le nombre de champs
num_champs = mysql_num_fields(result);
//on stock les valeurs de la ligne choisie
row = mysql_fetch_row(result);
//On déclare un pointeur long non signé pour y stocker la taille des valeurs
unsigned long *lengths;
//On stocke cette taille dans le pointeur
lengths = mysql_fetch_lengths(result);
//On fait une boucle pour avoir la valeur de chaque champs
for(i = 0; i < num_champs; i++)
{
//On ecrit toutes les valeurs
printf("[%.*s] ", (int) lengths[i], row[i] ? row[i] : "NULL");
}
|
Ce code me donne:
Code : Console
Voilà ce code ne me semble pas compliqué, on définit juste la ligne voulue puis on fait comme avant mais sans la boucle while, ce qui est normal puisque on a plus qu'un résultat à afficher.
Voilà nous avons terminé avec la théorie, nous allons maintenant passer à la pratique dans un petit TP, que de réjouissances en perspective.