Manipulation de MySQL avec C : Guide Pratique Complet
MySQL représente l'une des bases de données relationnelles open-source les plus répandues. Son intégration avec le langage C trouve des applications majeures dans la programmation système et le développement embarqué. Ce guide présente une approche exhaustive pour établir des connexions et réaliser les opérations fondamentales (création, lecture, modification, suppression) avec des exepmles de code détaillés.
1. Configuration de l'Environnement de Développement
1.1 Installation des Bibliothèques MySQL
Méthode recommandée pour systèmes Linux :
# Distributions basadasDebian/Ubuntu
sudo apt-get update
sudo apt-get install libmysqlclient-dev
# Distributions berbasisRPM (CentOS/RHEL)
sudo yum install mysql-devel
Alternative manuelle :
- Télécharger MySQL Connector/C depuis le site officiel
- Extraire l'archive
- Copier les fichiers headers et bibliothèques
# Extraction du paket
dpkg-deb -x libmysqlclient-dev_9.2.0-1ubuntu24.04_amd64.deb .
# Intégration des headers
cd usr
cp -r mysql/ /usr/include/
# Intégration des bibliothèques
cd /usr/lib/x86_64-linux-gnu
cp * -r /usr/lib/x86_64-linux-gnu/
1.2 Structure du Projet
Headers requis :
#include <mysql/mysql.h> // En-tête principal MySQL
#include <stdio.h> // Fonctions E/S standard
#include <stdlib.h> // Fonctions utilitaires standard
Commandes de compilation :
# Compilation basique avec chemins personnalisés
gcc application.c -o application -lmysqlclient \
-I/home/c/mysqldemo/usr/include/mysql \
-L/home/c/mysqldemo/usr/lib/x86_64-linux-gnu \
-lmysqlclient
# Gestion du plugin d'authentification pour MySQL 8 via Docker
docker cp 94:/usr/lib/mysql/plugin/mysql_native_password.so \
94:/usr/lib/mysql/plugin/
# Configuration Makefile complète
CC = gcc
CFLAGS = -Wall -g -std=c99
CPPFLAGS = -I/home/c/mysqldemo/usr/include/mysql
LDFLAGS = -L/home/c/mysqldemo/usr/lib/x86_64-linux-gnu
LDLIBS = -lmysqlclient -lssl -lcrypto -lpthread -lstdc++ -lz
CIBLE = application
SOURCES = application.c
OBJETS = $(SOURCES:.c=.o)
all: $(CIBLE)
$(CIBLE): $(OBJETS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
clean:
rm -f $(OBJETS) $(CIBLE)
2. Gestion des Connexions Base de Données
2.1 Établissement de la Connexion
// Définition de la structure de données
typedef struct {
MYSQL *connexion;
const char *serveur;
const char *utilisateur;
const char *motpasse;
const char *basededonnees;
unsigned int port;
} ConnexionBD;
int connecter_base(ConnexionBD *bd) {
bd->connexion = mysql_init(NULL);
if (!bd->connexion) {
traiter_erreur(bd->connexion, "Échec initialisation");
return -1;
}
// Configuration du timeout de connexion
unsigned int timeout = 5;
mysql_options(bd->connexion, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
mysql_options(bd->connexion, MYSQL_DEFAULT_AUTH, "mysql_native_password");
// Établissement de la connexion
if (!mysql_real_connect(
bd->connexion, bd->serveur, bd->utilisateur, bd->motpasse,
bd->basededonnees, bd->port, NULL, CLIENT_MULTI_STATEMENTS
)) {
traiter_erreur(bd->connexion, "Échec connexion");
mysql_close(bd->connexion);
return -1;
}
// Configuration de l'encodage caractères
if (mysql_set_character_set(bd->connexion, "utf8mb4")) {
traiter_erreur(bd->connexion, "Échec encodage");
mysql_close(bd->connexion);
return -1;
}
return 0;
}
2.2 Paramètres de Connexion
| Paramètre | Type | Description |
|---|---|---|
| serveur | const char* | Adresse de l'hôte (localhost pour local) |
| utilisateur | const char* | Nom d'utilisateur MySQL |
| motpasse | const char* | Mot de passe utilisateur |
| basededonnees | const char* | Nom de la base par défaut |
| port | unsgined int | Numéro de port |
3. Opérations CRUD en Pratique
3.1 Création d'une Table
// Requête de création de table
const char *requete_creation =
"CREATE TABLE IF NOT EXISTS articles ("
"id INT AUTO_INCREMENT PRIMARY KEY,"
"nom VARCHAR(100) NOT NULL,"
"prix DECIMAL(10,2) NOT NULL,"
"quantite INT DEFAULT 0"
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
if (mysql_query(bd.connexion, requete_creation)) {
traiter_erreur(bd.connexion, "Échec création table");
deconnecter_base(&bd);
return 1;
}
3.2 Insertion Sécurisée de Données
int inserer_compte(MYSQL *conn, const char *nomutilisateur,
const char *motdepasse, const char *courriel) {
MYSQL_STMT *instruction;
MYSQL_BIND liaison[3];
instruction = mysql_stmt_init(conn);
if (!instruction) {
fprintf(stderr, "Échec初始化STMT\n");
return -1;
}
const char *requete = "INSERT INTO comptes (nomutilisateur, motdepasse, courriel) "
"VALUES (?, ?, ?)";
if (mysql_stmt_prepare(instruction, requete, strlen(requete))) {
fprintf(stderr, "Échec préparation: %s\n", mysql_stmt_error(instruction));
mysql_stmt_close(instruction);
return -1;
}
// Initialisation des structures de liaison
memset(liaison, 0, sizeof(liaison));
// Liaison du nom d'utilisateur
liaison[0].buffer_type = MYSQL_TYPE_STRING;
liaison[0].buffer = (char *)nomutilisateur;
liaison[0].buffer_length = strlen(nomutilisateur);
liaison[0].is_null = 0;
// Liaison du mot de passe
liaison[1].buffer_type = MYSQL_TYPE_STRING;
liaison[1].buffer = (char *)motdepasse;
liaison[1].buffer_length = strlen(motdepasse);
liaison[1].is_null = 0;
// Liaison du courriel
liaison[2].buffer_type = MYSQL_TYPE_STRING;
liaison[2].buffer = (char *)courriel;
liaison[2].buffer_length = strlen(courriel);
liaison[2].is_null = (courriel == NULL) ? 1 : 0;
if (mysql_stmt_bind_param(instruction, liaison)) {
fprintf(stderr, "Échec liaison: %s\n", mysql_stmt_error(instruction));
mysql_stmt_close(instruction);
return -1;
}
if (mysql_stmt_execute(instruction)) {
fprintf(stderr, "Échec exécution: %s\n", mysql_stmt_error(instruction));
mysql_stmt_close(instruction);
return -1;
}
printf("Insertion réussie, ID: %llu\n", mysql_stmt_insert_id(instruction));
mysql_stmt_close(instruction);
return 0;
}
3.3 Requêtes SELECT et Traitement des Résultats
typedef struct {
int id;
char nomutilisateur[50];
char courriel[100];
char datecreation[20];
} Utilisateur;
int obtenir_comptes(MYSQL *conn, Utilisateur **comptes, int *nombre) {
if (mysql_query(conn, "SELECT id, nomutilisateur, courriel, datecreation FROM comptes")) {
fprintf(stderr, "Échec requête: %s\n", mysql_error(conn));
return -1;
}
MYSQL_RES *resultat = mysql_store_result(conn);
if (!resultat) {
fprintf(stderr, "Échec récupération结果: %s\n", mysql_error(conn));
return -1;
}
int nombre_champs = mysql_num_fields(resultat);
*nombre = mysql_num_rows(resultat);
if (*nombre == 0) {
mysql_free_result(resultat);
return 0;
}
*comptes = (Utilisateur *)malloc(sizeof(Utilisateur) * (*nombre));
if (!*comptes) {
fprintf(stderr, "Échec分配mémoire\n");
mysql_free_result(resultat);
return -1;
}
MYSQL_ROW ligne;
int index = 0;
while ((ligne = mysql_fetch_row(resultat))) {
(*comptes)[index].id = atoi(ligne[0]);
strncpy((*comptes)[index].nomutilisateur, ligne[1], 49);
if (ligne[2]) strncpy((*comptes)[index].courriel, ligne[2], 99);
if (ligne[3]) strncpy((*comptes)[index].datecreation, ligne[3], 19);
index++;
}
mysql_free_result(resultat);
return 0;
}
3.4 Opérations de Modification et Suppression
// Mise à jour du courriel d'un utilisateur
int mettre_a_jour_courriel(MYSQL *conn, int idutilisateur, const char *nouveau_courriel) {
MYSQL_STMT *instruction = mysql_stmt_init(conn);
if (!instruction) return -1;
const char *requete = "UPDATE comptes SET courriel = ? WHERE id = ?";
if (mysql_stmt_prepare(instruction, requete, strlen(requete))) {
mysql_stmt_close(instruction);
return -1;
}
MYSQL_BIND liaison[2];
memset(liaison, 0, sizeof(liaison));
// Lier le nouveau courriel
liaison[0].buffer_type = MYSQL_TYPE_STRING;
liaison[0].buffer = (char *)nouveau_courriel;
liaison[0].buffer_length = strlen(nouveau_courriel);
// Lier l'identifiant
liaison[1].buffer_type = MYSQL_TYPE_LONG;
liaison[1].buffer = &idutilisateur;
if (mysql_stmt_bind_param(instruction, liaison) ||
mysql_stmt_execute(instruction)) {
mysql_stmt_close(instruction);
return -1;
}
int lignes_affectees = mysql_stmt_affected_rows(instruction);
mysql_stmt_close(instruction);
if (lignes_affectees == 0) {
printf("Aucun utilisateur trouvé ou donnée inchangée\n");
return 0;
}
printf("Mise à jour réussie: %d ligne(s)\n", lignes_affectees);
return 1;
}
// Suppression d'un utilisateur
int supprimer_compte(MYSQL *conn, int idutilisateur) {
char requete[100];
snprintf(requete, sizeof(requete), "DELETE FROM comptes WHERE id = %d", idutilisateur);
if (mysql_query(conn, requete)) {
fprintf(stderr, "Échec suppression: %s\n", mysql_error(conn));
return -1;
}
int lignes_affectees = mysql_affected_rows(conn);
if (lignes_affectees == 0) {
printf("Aucun utilisateur trouvé\n");
return 0;
}
printf("Suppression réussie: %d ligne(s)\n", lignes_affectees);
return 1;
}
4. Fonctionnalités Avancées et Recommandations
4.1 Gestion des Transactions
int transferer_fonds(MYSQL *conn, int idsource, int idcible, double montant) {
// Démarrage de la transaction
if (mysql_query(conn, "START TRANSACTION")) {
return -1;
}
// Opération de débit
char requete[256];
snprintf(requete, sizeof(requete),
"UPDATE comptes SET solde =solde - %.2f WHERE idutilisateur = %d",
montant, idsource);
if (mysql_query(conn, requete)) {
mysql_query(conn, "ROLLBACK");
return -1;
}
// Opération de crédit
snprintf(requete, sizeof(requete),
"UPDATE comptes SET solde =solde + %.2f WHERE idutilisateur = %d",
montant, idcible);
if (mysql_query(conn, requete)) {
mysql_query(conn, "ROLLBACK");
return -1;
}
// Validation de la transaction
if (mysql_query(conn, "COMMIT")) {
mysql_query(conn, "ROLLBACK");
return -1;
}
return 0;
}
4.2 Implémentation d'un Pool de Connexions
#define MAX_CONNEXIONS 10
typedef struct {
MYSQL *connexion;
int en_utilisation;
time_t dernier_usage;
} ConnexionPool;
ConnexionPool pool_connexions[MAX_CONNEXIONS];
pthread_mutex_t mutex_pool = PTHREAD_MUTEX_INITIALIZER;
MYSQL *obtenir_connexion() {
pthread_mutex_lock(&mutex_pool);
MYSQL *conn = NULL;
int indice_ancien = -1;
time_t temps_ancien = time(NULL);
for (int i = 0; i < MAX_CONNEXIONS; i++) {
if (!pool_connexions[i].en_utilisation) {
if (pool_connexions[i].connexion) {
// Réutilisation d'une connexion existante
pool_connexions[i].en_utilisation = 1;
pool_connexions[i].dernier_usage = time(NULL);
conn = pool_connexions[i].connexion;
break;
} else {
// Création d'une nouvelle connexion
MYSQL *nouv_conn = mysql_init(NULL);
if (mysql_real_connect(nouv_conn, "localhost", "utilisateur", "motdepasse",
"basededonnees", 0, NULL, 0)) {
pool_connexions[i].connexion = nouv_conn;
pool_connexions[i].en_utilisation = 1;
pool_connexions[i].dernier_usage = time(NULL);
conn = nouv_conn;
break;
}
}
} else if (pool_connexions[i].dernier_usage < temps_ancien) {
indice_ancien = i;
temps_ancien = pool_connexions[i].dernier_usage;
}
}
pthread_mutex_unlock(&mutex_pool);
return conn;
}
void liberer_connexion(MYSQL *conn) {
pthread_mutex_lock(&mutex_pool);
for (int i = 0; i < MAX_CONNEXIONS; i++) {
if (pool_connexions[i].connexion == conn) {
pool_connexions[i].en_utilisation = 0;
break;
}
}
pthread_mutex_unlock(&mutex_pool);
}
4.3 Techniques d'Optimisation des Performancse
- Optimisation des insertions par lots :```
// Utilisation de la syntaxe multi-valeurs
mysql_query(conn, "INSERT INTO comptes (nomutilisateur, courriel) VALUES
('util1', 'u1@exemple.com'), ('util2', 'u2@exemple.com')");
- Configuration du cache de requêtes :```
// Activation du cache de requêtes
mysql_query(conn, "SET SESSION query_cache_type = ON");
- Optimisation des index :```
// Ajout d'index appropriés
mysql_query(conn, "ALTER TABLE comptes ADD INDEX idx_nomutilisateur (nomutilisateur)");
5. Gestion des Erreurs et Débogage
5.1 Mécanisme de Traitement des Erreurs
void traiter_erreur(MYSQL *conn) {
if (mysql_errno(conn)) {
fprintf(stderr, "Erreur MySQL %d: %s\n",
mysql_errno(conn), mysql_error(conn));
switch(mysql_errno(conn)) {
case CR_CONNECTION_ERROR:
// Traitement des erreurs de connexion
break;
case CR_SERVER_GONE_ERROR:
// Traitement de la déconnexion serveur
break;
case ER_DUP_ENTRY:
// Traitement des conflits de clé unique
break;
default:
// Autres erreurs
break;
}
}
}
5.2 Système de Journalisation
void journaliser_operation(const char *operation, const char *requete,
int lignes_affectees, time_t duree) {
FILE *fichier_journal = fopen("mysql.log", "a");
if (fichier_journal) {
fprintf(fichier_journal, "[%ld] %s: %s\n\tLignes affectées: %d, Durée: %ldms\n",
time(NULL), operation, requete, lignes_affectees, duree);
fclose(fichier_journal);
}
}
6. Mesures de Sécurité
6.1 Protection contre les Injections SQL
- Toujours utiliser des instructions préparées
- Validation des entrées :```
int valider_entree(const char *input) {
const char *caracteres_danger = "'";
return strpbrk(input, caracteres_danger) == NULL;
}
- Principe du moindre privilège :```
// Utilisation d'un compte avec privilèges limités
mysql_real_connect(conn, "localhost", "utilisateur_application", "motdepasse_limite",
"basededonnees_application", 0, NULL, 0);
6.2 Protection des Données Sensibles
// Stockage chiffré des mots de passe
void sauvegarder_motpasse(MYSQL *conn, const char *nomutilisateur, const char *motdepasse) {
char motpasse_hache[61]; // Longueur hash bcrypt
bcrypt_hashpw(motdepasse, bcrypt_gensalt(12), motpasse_hache);
// Stockage via instruction préparée
MYSQL_STMT *instruction = mysql_stmt_init(conn);
const char *requete = "UPDATE comptes SET motdepasse = ? WHERE nomutilisateur = ?";
// ... liaison des paramètres et exécution
}
Exemple Complet
// application.c
#include <mysql/mysql.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
MYSQL *connexion;
const char *serveur;
const char *utilisateur;
const char *motpasse;
const char *basededonnees;
unsigned int port;
} ConnexionBD;
void traiter_erreur(MYSQL *conn, const char *msg) {
if (mysql_errno(conn)) {
fprintf(stderr, "%s: Erreur MySQL %d: %s\n",
msg, mysql_errno(conn), mysql_error(conn));
}
}
int connecter_base(ConnexionBD *bd) {
bd->connexion = mysql_init(NULL);
if (!bd->connexion) {
traiter_erreur(bd->connexion, "Échec initialisation");
return -1;
}
// Configuration du timeout et du plugin d'authentification
unsigned int timeout = 5;
mysql_options(bd->connexion, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
mysql_options(bd->connexion, MYSQL_DEFAULT_AUTH, "mysql_native_password");
if (!mysql_real_connect(
bd->connexion, bd->serveur, bd->utilisateur, bd->motpasse,
bd->basededonnees, bd->port, NULL, CLIENT_MULTI_STATEMENTS
)) {
traiter_erreur(bd->connexion, "Échec connexion");
mysql_close(bd->connexion);
return -1;
}
// Configuration de l'encodage
if (mysql_set_character_set(bd->connexion, "utf8mb4")) {
traiter_erreur(bd->connexion, "Échec encodage");
mysql_close(bd->connexion);
return -1;
}
return 0;
}
void deconnecter_base(ConnexionBD *bd) {
if (bd->connexion) {
mysql_close(bd->connexion);
bd->connexion = NULL;
}
}
void afficher_resultats(MYSQL_RES *resultat) {
if (!resultat) return;
int nombre_champs = mysql_num_fields(resultat);
MYSQL_FIELD *champs = mysql_fetch_fields(resultat);
MYSQL_ROW ligne;
// Affichage des=en-têtes
for (int i = 0; i < nombre_champs; i++) {
printf("%-15s", champs[i].name);
}
printf("\n");
// Affichage des séparateurs
for (int i = 0; i < nombre_champs; i++) {
printf("---------------");
}
printf("\n");
// Affichage des données
while ((ligne = mysql_fetch_row(resultat))) {
for (int i = 0; i < nombre_champs; i++) {
printf("%-15s", ligne[i] ? ligne[i] : "NULL");
}
printf("\n");
}
}
int main() {
ConnexionBD bd = {
.serveur = "127.0.0.1",
.utilisateur = "root",
.motpasse = "123456",
.basededonnees = "test_db",
.port = 3306
};
printf("Connexion à la base %s:%d en cours...\n", bd.serveur, bd.port);
if (connecter_base(&bd)) return 1;
printf("Connexion établie avec succès\n\n");
// Création de la table
const char *requete_creation =
"CREATE TABLE IF NOT EXISTS articles ("
"id INT AUTO_INCREMENT PRIMARY KEY,"
"nom VARCHAR(100) NOT NULL,"
"prix DECIMAL(10,2) NOT NULL,"
"quantite INT DEFAULT 0"
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
if (mysql_query(bd.connexion, requete_creation)) {
traiter_erreur(bd.connexion, "Échec création table");
deconnecter_base(&bd);
return 1;
}
printf("Table créée avec succès\n\n");
// Insertion de données (utilisation simplifiée pour démonstration)
const char *requete_insertion =
"INSERT INTO articles (nom, prix, quantite) VALUES "
"('Ordinateur portable', 9999.99, 10),"
"('Smartphone', 5999.99, 20),"
"('Tablette', 3999.99, 15)";
if (mysql_query(bd.connexion, requete_insertion)) {
traiter_erreur(bd.connexion, "Échec insertion");
deconnecter_base(&bd);
return 1;
}
printf("Données insérées, lignes affectées: %lu\n\n", mysql_affected_rows(bd.connexion));
// Requête de sélection
if (mysql_query(bd.connexion, "SELECT * FROM articles")) {
traiter_erreur(bd.connexion, "Échec requête");
deconnecter_base(&bd);
return 1;
}
printf("Requête réussie\n");
afficher_resultats(mysql_store_result(bd.connexion));
// Libération propre des ressources
deconnecter_base(&bd);
printf("\nConnexion fermée\n");
return 0;
}