Manipulation de MySQL avec C : Guide Pratique Complet

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 :

  1. Télécharger MySQL Connector/C depuis le site officiel
  2. Extraire l'archive
  3. 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

  1. 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')");
  2. Configuration du cache de requêtes :``` // Activation du cache de requêtes mysql_query(conn, "SET SESSION query_cache_type = ON");
  3. 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

  1. Toujours utiliser des instructions préparées
  2. Validation des entrées :``` int valider_entree(const char *input) { const char *caracteres_danger = "'"; return strpbrk(input, caracteres_danger) == NULL; }
  3. 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;
}

Publié le 23 juillet à 01h46