From 86ba53836168bcc591f532f2419fa290de601572 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Wed, 8 May 2019 11:28:58 +0200
Subject: Updated the core functions dealing with SQLite databases.

---
 src/analysis/db/collection.c     | 291 +++++------------------------
 src/analysis/db/item-int.h       |   8 +-
 src/analysis/db/item.c           | 113 +++++++-----
 src/analysis/db/item.h           |   6 +-
 src/analysis/db/items/bookmark.c |  74 ++++----
 src/analysis/db/items/comment.c  | 183 +++++++++++--------
 src/analysis/db/items/move.c     |  80 ++++----
 src/analysis/db/items/switcher.c | 129 +++++++------
 src/analysis/db/misc/rlestr.c    | 101 ++++-------
 src/analysis/db/misc/rlestr.h    |   9 +-
 src/analysis/db/misc/timestamp.c |  69 +++----
 src/analysis/db/misc/timestamp.h |   9 +-
 src/arch/vmpa.c                  |  81 +++------
 src/arch/vmpa.h                  |   5 +-
 src/common/sqlite.c              | 382 +++++++++++++++++++++++++++++++++++++++
 src/common/sqlite.h              |  12 ++
 src/glibext/gbinarycursor.c      |  38 +---
 src/glibext/gbinarycursor.h      |   4 +-
 src/glibext/glinecursor-int.h    |   8 +-
 src/glibext/glinecursor.c        |   6 +-
 src/glibext/glinecursor.h        |   2 +-
 21 files changed, 885 insertions(+), 725 deletions(-)

diff --git a/src/analysis/db/collection.c b/src/analysis/db/collection.c
index 0a617f3..cdc91aa 100644
--- a/src/analysis/db/collection.c
+++ b/src/analysis/db/collection.c
@@ -67,6 +67,9 @@ static bool _g_db_collection_setup_load(GDbCollection *, bound_value **, size_t
 /* Décrit les colonnes utiles à un chargement de données. */
 static bool g_db_collection_setup_load(GDbCollection *, bound_value **, size_t *);
 
+/* Charge et intère un élément dans une collection. */
+static bool g_db_collection_load_new_item(const bound_value *, size_t, GDbCollection *);
+
 /* Enregistre un élément de collection dans une base de données. */
 static bool g_db_collection_store_item(const GDbCollection *, const GDbItem *, sqlite3 *);
 
@@ -961,16 +964,16 @@ bool g_db_collection_create_db_table(const GDbCollection *collec, sqlite3 *db)
 
 static bool _g_db_collection_setup_load(GDbCollection *collec, bound_value **values, size_t *count)
 {
-    if (!setup_load_of_timestamp(NULL, "created", values, count))
+    if (!store_timestamp(NULL, "created", values, count))
         return false;
 
-    if (!setup_load_of_timestamp(NULL, "timestamp", values, count))
+    if (!store_timestamp(NULL, "timestamp", values, count))
         return false;
 
-    if (!setup_load_of_rle_string(NULL, "author", values, count))
+    if (!store_rle_string(NULL, "author", values, count))
         return false;
 
-    if (!setup_load_of_rle_string(NULL, "tool", values, count))
+    if (!store_rle_string(NULL, "tool", values, count))
         return false;
 
     return true;
@@ -1004,157 +1007,63 @@ static bool g_db_collection_setup_load(GDbCollection *collec, bound_value **valu
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : collec = ensemble d'éléments à peupler.                      *
-*                db     = base de données repondant aux requêtes.             *
+*  Paramètres  : values = couples de champs et de valeurs à lier.             *
+*                count  = nombre de ces couples.                              *
+*                collec = collection à manipuler.                             *
 *                                                                             *
-*  Description : Charge un ensemble d'éléments à partir d'une base de données.*
+*  Description : Charge et intère un élément dans une collection.             *
 *                                                                             *
-*  Retour      : Bilan de l'exécution de l'opération.                         *
+*  Retour      : Bilan de l'opération : succès ou non.                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_collection_load_all_items(GDbCollection *collec, sqlite3 *db)
+static bool g_db_collection_load_new_item(const bound_value *values, size_t count, GDbCollection *collec)
 {
-    bool result;                            /* Conclusion à faire remonter */
-    bound_value *values;                    /* Champs de table à inclure   */
-    size_t count;                           /* Nombre de ces champs        */
-    char *sql;                              /* Requête SQL à construire    */
-    size_t i;                               /* Boucle de parcours          */
-    sqlite3_stmt *stmt;                     /* Déclaration mise en place   */
-    int ret;                                /* Bilan d'un appel à SQLite   */
-    int native_type;                        /* Type de valeur dans la base */
+    bool result;                            /* Bilan à retourner           */
     GDbItem *new;                           /* Nouvel élément à insérer    */
 
-    result = false;
-
-    if (!g_db_collection_setup_load(collec, &values, &count))
-        goto gdclai_building_values;
-
-    /* Préparation de la requête */
+    new = g_object_new(G_DB_COLLECTION(collec)->type, NULL);
 
-    sql = strdup("SELECT ");
+    result = g_db_item_load(new, values, count);
 
-    for (i = 0; i < count; i++)
+    if (result)
     {
-        if (i > 0) sql = stradd(sql, ", ");
+        result = g_db_collection_add_item(G_DB_COLLECTION(collec), new);
 
-        sql = stradd(sql, values[i].name);
+        g_object_unref(G_OBJECT(new));
 
     }
 
-    sql = stradd(sql, " FROM ");
-    sql = stradd(sql, collec->name);
-    sql = stradd(sql, ";");
-
-    ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
-	if (ret != SQLITE_OK)
-    {
-		fprintf(stderr, "Can't prepare SELECT statment '%s' (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
-        goto gdclai_exit;
-	}
-
-    /* Chargement des valeurs existantes */
-
-    result = true;
-
-    for (ret = sqlite3_step(stmt); ret == SQLITE_ROW && result; ret = sqlite3_step(stmt))
-    {
-        /* Conversion des valeurs */
-
-        for (i = 0; i < count; i++)
-        {
-            native_type = sqlite3_column_type(stmt, i);
-
-            /**
-             * On réalise une petite conversion selon le champ.
-             *
-             * Le filtre SQLITE_NATIVE est destiné à conserver le type choisi par
-             * SQLite. Typiquement, une chaîne peut être à SQLITE_NULL ou SQLITE_TEXT
-             * selon la valeur conservée dans la base.
-             *
-             * D'autres éléments, comme les localisations en mémoire, peuvent aussi
-             * avoir un champ éventuellement nul, donc la définition à partir des
-             * indications de la base de données reste importante.
-             *
-             * En ce qui concerne les valeurs numériques, SQLite ne fait pas de
-             * distinction : tout passe par la fonction sqlite3VdbeIntValue(),
-             * qui effectue des transtypages au besoin pour tout ce qui n'est
-             * pas numérique.
-             *
-             * Pour les types internes SQLITE_INTEGER et SQLITE_BOOLEAN,
-             * il est donc nécessaire d'ajuster en interne.
-             */
-
-            if (native_type == SQLITE_INTEGER)
-                native_type = SQLITE_INT64;
-
-            if (values[i].type == SQLITE_NATIVE)
-                values[i].type = native_type;
-
-            else
-                assert(values[i].type == native_type
-                       || values[i].type == SQLITE_INTEGER
-                       || values[i].type == SQLITE_BOOLEAN);
-
-            switch (values[i].type)
-            {
-                case SQLITE_BOOLEAN:
-                    values[i].boolean = (bool)sqlite3_column_int(stmt, i);
-                    break;
-
-                case SQLITE_INTEGER:
-                    values[i].integer = sqlite3_column_int(stmt, i);
-                    break;
-
-                case SQLITE_INT64:
-                    values[i].integer64 = sqlite3_column_int64(stmt, i);
-                    break;
-
-                case SQLITE_FLOAT:
-                    assert(0);
-                    break;
-
-                case SQLITE_TEXT:
-                    values[i].cstring = (const char *)sqlite3_column_text(stmt, i);
-                    break;
-
-                case SQLITE_BLOB:
-                    assert(0);
-                    break;
-
-                case SQLITE_NULL:
-                    break;
-
-                default:
-                    assert(0);
-                    break;
-
-            }
-
-        }
-
-        /* Chargement d'un nouvel élément */
-
-        new = g_object_new(G_DB_COLLECTION(collec)->type, NULL);
-
-        result = g_db_item_load(new, values, count);
-        result &= g_db_collection_add_item(G_DB_COLLECTION(collec), new);
-
-        g_object_unref(G_OBJECT(new));
+    return result;
 
-    }
+}
 
-    /* Sortie propre */
 
-    sqlite3_finalize(stmt);
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à peupler.                      *
+*                db     = base de données repondant aux requêtes.             *
+*                                                                             *
+*  Description : Charge un ensemble d'éléments à partir d'une base de données.*
+*                                                                             *
+*  Retour      : Bilan de l'exécution de l'opération.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
 
- gdclai_exit:
+bool g_db_collection_load_all_items(GDbCollection *collec, sqlite3 *db)
+{
+    bool result;                            /* Conclusion à faire remonter */
+    bound_value *values;                    /* Champs de table à inclure   */
+    size_t count;                           /* Nombre de ces champs        */
 
-    free(sql);
+    result = g_db_collection_setup_load(collec, &values, &count);
 
- gdclai_building_values:
+    if (result)
+        result = load_db_values(db, collec->name, values, count, (db_load_cb)g_db_collection_load_new_item, collec);
 
     free_all_bound_values(values, count);
 
@@ -1182,121 +1091,11 @@ static bool g_db_collection_store_item(const GDbCollection *collec, const GDbIte
     bool result;                            /* Conclusion à faire remonter */
     bound_value *values;                    /* Champs de table à inclure   */
     size_t count;                           /* Nombre de ces champs        */
-    char *sql;                              /* Requête SQL à construire    */
-    size_t i;                               /* Boucle de parcours          */
-    sqlite3_stmt *stmt;                     /* Déclaration mise en place   */
-    int ret;                                /* Bilan d'un appel à SQLite   */
-    int index;                              /* Indice de valeur attachée   */
 
-    result = false;
-
-    if (!g_db_item_prepare_db_statement(item, &values, &count))
-        goto gdcsi_building_values;
-
-    /* Préparation de la requête */
-
-    sql = strdup("INSERT INTO ");
-    sql = stradd(sql, collec->name);
-    sql = stradd(sql, " (");
+    result = g_db_item_store(item, &values, &count);
 
-    for (i = 0; i < count; i++)
-    {
-        if (i > 0) sql = stradd(sql, ", ");
-
-        sql = stradd(sql, values[i].name);
-
-    }
-
-    sql = stradd(sql, ") VALUES (");
-
-    for (i = 0; i < count; i++)
-    {
-        if (i > 0) sql = stradd(sql, ", ");
-
-        if (values[i].type == SQLITE_RAW)
-            sql = stradd(sql, values[i].cstring);
-        else
-            sql = stradd(sql, "?");
-
-    }
-
-    sql = stradd(sql, ");");
-
-	ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
-	if (ret != SQLITE_OK)
-    {
-		fprintf(stderr, "Can't prepare INSERT statment '%s' (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
-        goto gdcsi_exit;
-	}
-
-    /* Attribution des valeurs */
-
-    index = 1;
-
-    for (i = 0; i < count; i++)
-    {
-        switch (values[i].type)
-        {
-            case SQLITE_BOOLEAN:
-                ret = sqlite3_bind_int(stmt, index, values[i].boolean);
-                index++;
-                break;
-
-            case SQLITE_INTEGER:
-                ret = sqlite3_bind_int(stmt, index, values[i].integer);
-                index++;
-                break;
-
-            case SQLITE_INT64:
-                ret = sqlite3_bind_int64(stmt, index, values[i].integer64);
-                index++;
-                break;
-
-            case SQLITE_TEXT:
-                ret = sqlite3_bind_text(stmt, index, values[i].string, -1, values[i].delete);
-                index++;
-                break;
-
-            case SQLITE_NULL:
-                ret = sqlite3_bind_null(stmt, index);
-                index++;
-                break;
-
-            default:
-                assert(false);
-                ret = SQLITE_ERROR;
-                break;
-
-        }
-
-        if (ret != SQLITE_OK)
-        {
-            fprintf(stderr, "Can't bind value for parameter nb %d in '%s' (ret=%d): %s\n",
-                    index - 1, sql, ret, sqlite3_errmsg(db));
-            goto gdcsi_exit;
-        }
-
-    }
-
-    /* Exécution finale */
-
-	ret = sqlite3_step(stmt);
-
-    if (ret != SQLITE_DONE)
-    {
-		fprintf(stderr, "INSERT statement '%s' didn't return DONE (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
-        goto gdcsi_exit;
-    }
-
-    sqlite3_finalize(stmt);
-
-    result = true;
-
- gdcsi_exit:
-
-    free(sql);
-
- gdcsi_building_values:
+    if (result)
+        result = store_db_values(db, collec->name, values, count);
 
     free_all_bound_values(values, count);
 
@@ -1334,7 +1133,7 @@ static bool g_db_collection_store_updated_item(const GDbCollection *collec, cons
 
     result = false;
 
-    if (!g_db_item_prepare_db_statement(item, &values, &count))
+    if (!g_db_item_store(item, &values, &count))
         goto gdcsui_building_values;
 
     /* Préparation de la requête */
diff --git a/src/analysis/db/item-int.h b/src/analysis/db/item-int.h
index 618bab9..3a5f47d 100644
--- a/src/analysis/db/item-int.h
+++ b/src/analysis/db/item-int.h
@@ -51,12 +51,12 @@ typedef void (* build_item_label_fc) (GDbItem *);
 /* Exécute un élément de collection sur un binaire. */
 typedef bool (* run_item_fc) (GDbItem *, GLoadedBinary *);
 
-/* Constitue les champs destinés à une insertion / modification. */
-typedef bool (* prepare_db_statement) (const GDbItem *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour une localisation. */
 typedef bool (* load_db_item_fc) (GDbItem *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+typedef bool (* store_db_item_fc) (const GDbItem *, bound_value **, size_t *);
+
 
 /* Base d'un élément pour collection générique (instance) */
 struct _GDbItem
@@ -91,8 +91,8 @@ struct _GDbItemClass
     run_item_fc apply;                      /* Application de l'élément    */
     run_item_fc cancel;                     /* Retrait de l'élément        */
 
-    prepare_db_statement prepare_stmt;      /* Préparation d'une requête   */
     load_db_item_fc load;                   /* Chargement à partir d'une BD*/
+    store_db_item_fc store;                 /* Préparation d'une requête   */
 
 };
 
diff --git a/src/analysis/db/item.c b/src/analysis/db/item.c
index f01a953..6fa168f 100644
--- a/src/analysis/db/item.c
+++ b/src/analysis/db/item.c
@@ -58,12 +58,12 @@ static bool _g_db_item_pack(const GDbItem *, packed_buffer *);
 /* --------------------- MANIPULATIONS AVEC UNE BASE DE DONNEES --------------------- */
 
 
-/* Constitue les champs destinés à une insertion / modification. */
-static bool _g_db_item_prepare_db_statement(const GDbItem *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour un élément de collection. */
 static bool _g_db_item_load(GDbItem *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+static bool _g_db_item_store(const GDbItem *, bound_value **, size_t *);
+
 
 
 /* Indique le type défini pour une base d'élément de collection générique. */
@@ -96,8 +96,8 @@ static void g_db_item_class_init(GDbItemClass *klass)
     klass->unpack = (unpack_db_item_fc)_g_db_item_unpack;
     klass->pack = (pack_db_item_fc)_g_db_item_pack;
 
-    klass->prepare_stmt = (prepare_db_statement)_g_db_item_prepare_db_statement;
     klass->load = (load_db_item_fc)_g_db_item_load;
+    klass->store = (store_db_item_fc)_g_db_item_store;
 
 }
 
@@ -616,31 +616,31 @@ bool g_db_item_is_volatile(const GDbItem *item)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : item   = base d'éléments sur laquelle s'appuyer.             *
-*                values = couples de champs et de valeurs à lier. [OUT]       *
-*                count  = nombre de ces couples. [OUT]                        *
+*  Paramètres  : item   = base d'éléments à charger depuis les réponses.      *
+*                values = tableau d'éléments à consulter.                     *
+*                count  = nombre de descriptions renseignées.                 *
 *                                                                             *
-*  Description : Constitue les champs destinés à une insertion / modification.*
+*  Description : Charge les valeurs utiles pour un élément de collection.     *
 *                                                                             *
-*  Retour      : Bilan de l'opération : succès ou non.                        *
+*  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool _g_db_item_prepare_db_statement(const GDbItem *item, bound_value **values, size_t *count)
+static bool _g_db_item_load(GDbItem *item, const bound_value *values, size_t count)
 {
-    bool result;                            /* Bilan à retourner           */
+    bool result;                            /* Bilan global à retourner    */
 
     result = true;
 
-    result &= prepare_db_statement_for_timestamp(&item->created, "created", values, count);
+    result &= load_timestamp(&item->created, "created", values, count);
 
-    result &= prepare_db_statement_for_timestamp(&item->timestamp, "timestamp", values, count);
+    result &= load_timestamp(&item->timestamp, "timestamp", values, count);
 
-    result &= prepare_db_statement_for_rle_string(&item->author, "author", values, count);
+    result &= load_rle_string(&item->author, "author", values, count);
 
-    result &= prepare_db_statement_for_rle_string(&item->tool, "tool", values, count);
+    result &= load_rle_string(&item->tool, "tool", values, count);
 
     return result;
 
@@ -649,55 +649,75 @@ static bool _g_db_item_prepare_db_statement(const GDbItem *item, bound_value **v
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : item   = base d'éléments sur laquelle s'appuyer.             *
-*                values = couples de champs et de valeurs à lier. [OUT]       *
-*                count  = nombre de ces couples. [OUT]                        *
+*  Paramètres  : item   = base d'éléments à charger depuis les réponses.      *
+*                values = tableau d'éléments à consulter.                     *
+*                count  = nombre de descriptions renseignées.                 *
 *                                                                             *
-*  Description : Constitue les champs destinés à une insertion / modification.*
+*  Description : Charge les valeurs utiles pour un élément de collection.     *
 *                                                                             *
-*  Retour      : Bilan de l'opération : succès ou non.                        *
+*  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_item_prepare_db_statement(const GDbItem *item, bound_value **values, size_t *count)
+bool g_db_item_load(GDbItem *item, const bound_value *values, size_t count)
 {
-    *values = NULL;
-    *count = 0;
+    bool result;                            /* Bilan à retourner           */
+
+    result = G_DB_ITEM_GET_CLASS(item)->load(item, values, count);
 
-    return G_DB_ITEM_GET_CLASS(item)->prepare_stmt(item, values, count);
+    return result;
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : item   = base d'éléments à charger depuis les réponses.      *
-*                values = tableau d'éléments à consulter.                     *
-*                count  = nombre de descriptions renseignées.                 *
+*  Paramètres  : item   = base d'éléments sur laquelle s'appuyer.             *
+*                values = couples de champs et de valeurs à lier. [OUT]       *
+*                count  = nombre de ces couples. [OUT]                        *
 *                                                                             *
-*  Description : Charge les valeurs utiles pour un élément de collection.     *
+*  Description : Constitue les champs destinés à une insertion / modification.*
 *                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
+*  Retour      : Bilan de l'opération : succès ou non.                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool _g_db_item_load(GDbItem *item, const bound_value *values, size_t count)
+static bool _g_db_item_store(const GDbItem *item, bound_value **values, size_t *count)
 {
-    bool result;                            /* Bilan global à retourner    */
-
-    result = true;
+    bool result;                            /* Bilan à retourner           */
 
-    result &= load_timestamp(&item->created, "created", values, count);
+    if (item == NULL)
+        result = store_timestamp(NULL, "created", values, count);
+    else
+        result = store_timestamp(&item->created, "created", values, count);
 
-    result &= load_timestamp(&item->timestamp, "timestamp", values, count);
+    if (result)
+    {
+        if (item == NULL)
+            result = store_timestamp(NULL, "timestamp", values, count);
+        else
+            result = store_timestamp(&item->timestamp, "timestamp", values, count);
+    }
 
-    result &= load_rle_string(&item->author, "author", values, count);
+    if (result)
+    {
+        if (item == NULL)
+            result = store_rle_string(NULL, "author", values, count);
+        else
+            result = store_rle_string(&item->author, "author", values, count);
+    }
 
-    result &= load_rle_string(&item->tool, "tool", values, count);
+    if (result)
+    {
+        if (item == NULL)
+            result = store_rle_string(NULL, "tool", values, count);
+        else
+            result = store_rle_string(&item->tool, "tool", values, count);
+    }
 
     return result;
 
@@ -706,24 +726,23 @@ static bool _g_db_item_load(GDbItem *item, const bound_value *values, size_t cou
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : item   = base d'éléments à charger depuis les réponses.      *
-*                values = tableau d'éléments à consulter.                     *
-*                count  = nombre de descriptions renseignées.                 *
+*  Paramètres  : item   = base d'éléments sur laquelle s'appuyer.             *
+*                values = couples de champs et de valeurs à lier. [OUT]       *
+*                count  = nombre de ces couples. [OUT]                        *
 *                                                                             *
-*  Description : Charge les valeurs utiles pour un élément de collection.     *
+*  Description : Constitue les champs destinés à une insertion / modification.*
 *                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
+*  Retour      : Bilan de l'opération : succès ou non.                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_item_load(GDbItem *item, const bound_value *values, size_t count)
+bool g_db_item_store(const GDbItem *item, bound_value **values, size_t *count)
 {
-    bool result;                            /* Bilan à retourner           */
-
-    result = G_DB_ITEM_GET_CLASS(item)->load(item, values, count);
+    *values = NULL;
+    *count = 0;
 
-    return result;
+    return G_DB_ITEM_GET_CLASS(item)->store(item, values, count);
 
 }
diff --git a/src/analysis/db/item.h b/src/analysis/db/item.h
index 1cb0a0b..6644507 100644
--- a/src/analysis/db/item.h
+++ b/src/analysis/db/item.h
@@ -108,12 +108,12 @@ bool g_db_item_is_volatile(const GDbItem *);
 /* --------------------- MANIPULATIONS AVEC UNE BASE DE DONNEES --------------------- */
 
 
-/* Constitue les champs destinés à une insertion / modification. */
-bool g_db_item_prepare_db_statement(const GDbItem *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour un élément de collection. */
 bool g_db_item_load(GDbItem *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+bool g_db_item_store(const GDbItem *, bound_value **, size_t *);
+
 
 
 #endif  /* _ANALYSIS_DB_ITEM_H */
diff --git a/src/analysis/db/items/bookmark.c b/src/analysis/db/items/bookmark.c
index d3010dd..5d6ccfa 100644
--- a/src/analysis/db/items/bookmark.c
+++ b/src/analysis/db/items/bookmark.c
@@ -93,12 +93,12 @@ static bool g_db_bookmark_apply(GDbBookmark *, GLoadedBinary *);
 /* Annule un signet sur un tampon de binaire chargé. */
 static bool g_db_bookmark_cancel(GDbBookmark *, GLoadedBinary *);
 
-/* Constitue les champs destinés à une insertion / modification. */
-static bool g_db_bookmark_prepare_db_statement(const GDbBookmark *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour un signet. */
 static bool g_db_bookmark_load(GDbBookmark *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+static bool g_db_bookmark_store(const GDbBookmark *, bound_value **, size_t *);
+
 
 
 /* ---------------------- DEFINITION DE LA COLLECTION ASSOCIEE ---------------------- */
@@ -186,8 +186,8 @@ static void g_db_bookmark_class_init(GDbBookmarkClass *klass)
     item->apply = (run_item_fc)g_db_bookmark_apply;
     item->cancel = (run_item_fc)g_db_bookmark_cancel;
 
-    item->prepare_stmt = (prepare_db_statement)g_db_bookmark_prepare_db_statement;
     item->load = (load_db_item_fc)g_db_bookmark_load;
+    item->store = (store_db_item_fc)g_db_bookmark_store;
 
 }
 
@@ -492,61 +492,73 @@ static bool g_db_bookmark_cancel(GDbBookmark *bookmark, GLoadedBinary *binary)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : bookmark = base d'éléments sur laquelle s'appuyer.           *
-*                values   = couples de champs et de valeurs à lier. [OUT]     *
-*                count    = nombre de ces couples. [OUT]                      *
+*  Paramètres  : bookmark = bascule d'affichage à charger depuis les réponses.*
+*                values   = tableau d'éléments à consulter.                   *
+*                count    = nombre de descriptions renseignées.               *
 *                                                                             *
-*  Description : Constitue les champs destinés à une insertion / modification.*
+*  Description : Charge les valeurs utiles pour un signet.                    *
 *                                                                             *
-*  Retour      : Etat du besoin en sauvegarde.                                *
+*  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool g_db_bookmark_prepare_db_statement(const GDbBookmark *bookmark, bound_value **values, size_t *count)
+static bool g_db_bookmark_load(GDbBookmark *bookmark, const bound_value *values, size_t count)
 {
-    bool status;                            /* Bilan d'opération initiale  */
+    bool result;                            /* Bilan à faire remonter      */
 
-    status = G_DB_ITEM_CLASS(g_db_bookmark_parent_class)->prepare_stmt(G_DB_ITEM(bookmark), values, count);
-    if (!status) return false;
+    result = G_DB_ITEM_CLASS(g_db_bookmark_parent_class)->load(G_DB_ITEM(bookmark), values, count);
 
-    status = prepare_vmpa_db_statement(&bookmark->addr, NULL, values, count);
-    if (!status) return false;
+    result &= load_vmpa(&bookmark->addr, NULL, values, count);
 
-    status &= prepare_db_statement_for_rle_string(&bookmark->comment, "comment", values, count);
-    if (!status) return false;
+    result &= load_rle_string(&bookmark->comment, "comment", values, count);
 
-    return true;
+    return result;
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : bookmark = bascule d'affichage à charger depuis les réponses.*
-*                values   = tableau d'éléments à consulter.                   *
-*                count    = nombre de descriptions renseignées.               *
+*  Paramètres  : bookmark = base d'éléments sur laquelle s'appuyer.           *
+*                values   = couples de champs et de valeurs à lier. [OUT]     *
+*                count    = nombre de ces couples. [OUT]                      *
 *                                                                             *
-*  Description : Charge les valeurs utiles pour un signet.                    *
+*  Description : Constitue les champs destinés à une insertion / modification.*
 *                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
+*  Retour      : Etat du besoin en sauvegarde.                                *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool g_db_bookmark_load(GDbBookmark *bookmark, const bound_value *values, size_t count)
+static bool g_db_bookmark_store(const GDbBookmark *bookmark, bound_value **values, size_t *count)
 {
-    bool result;                            /* Bilan à faire remonter      */
+    bool status;                            /* Bilan d'opération initiale  */
 
-    result = G_DB_ITEM_CLASS(g_db_bookmark_parent_class)->load(G_DB_ITEM(bookmark), values, count);
+    if (bookmark == NULL)
+        status = G_DB_ITEM_CLASS(g_db_bookmark_parent_class)->store(NULL, values, count);
+    else
+        status = G_DB_ITEM_CLASS(g_db_bookmark_parent_class)->store(G_DB_ITEM(bookmark), values, count);
 
-    result &= load_vmpa(&bookmark->addr, NULL, values, count);
+    if (!status) return false;
 
-    result &= load_rle_string(&bookmark->comment, "comment", values, count);
+    if (bookmark == NULL)
+        status = store_vmpa(NULL, NULL, values, count);
+    else
+        status = store_vmpa(&bookmark->addr, NULL, values, count);
 
-    return result;
+    if (!status) return false;
+
+    if (bookmark == NULL)
+        status &= store_rle_string(NULL, "comment", values, count);
+    else
+        status &= store_rle_string(&bookmark->comment, "comment", values, count);
+
+    if (!status) return false;
+
+    return true;
 
 }
 
@@ -801,10 +813,10 @@ static bool g_bookmark_collection_setup_load(GBookmarkCollection *collec, bound_
                                                                                    values, count);
     if (!status) return false;
 
-    if (!setup_load_for_vmpa(NULL, values, count))
+    if (!store_vmpa(NULL, NULL, values, count))
         return false;
 
-    if (!setup_load_of_rle_string(NULL, "comment", values, count))
+    if (!store_rle_string(NULL, "comment", values, count))
         return false;
 
     return true;
diff --git a/src/analysis/db/items/comment.c b/src/analysis/db/items/comment.c
index 7eb7906..6ac37fd 100644
--- a/src/analysis/db/items/comment.c
+++ b/src/analysis/db/items/comment.c
@@ -113,12 +113,12 @@ static bool g_db_comment_apply(GDbComment *, GLoadedBinary *);
 /* Annule l'impression d'un commentaire dans du code de binaire. */
 static bool g_db_comment_cancel(GDbComment *, GLoadedBinary *);
 
-/* Constitue les champs destinés à une insertion / modification. */
-static bool g_db_comment_prepare_db_statement(GDbComment *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour un commentaire. */
 static bool g_db_comment_load(GDbComment *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+static bool g_db_comment_store(GDbComment *, bound_value **, size_t *);
+
 /* Associe un contenu formaté supplémentaire à un commentaire. */
 static void g_db_comment_add_rle_string(GDbComment *, const rle_string *);
 
@@ -236,8 +236,8 @@ static void g_db_comment_class_init(GDbCommentClass *klass)
     item->apply = (run_item_fc)g_db_comment_apply;
     item->cancel = (run_item_fc)g_db_comment_cancel;
 
-    item->prepare_stmt = (prepare_db_statement)g_db_comment_prepare_db_statement;
     item->load = (load_db_item_fc)g_db_comment_load;
+    item->store = (store_db_item_fc)g_db_comment_store;
 
 }
 
@@ -694,8 +694,7 @@ static bool g_db_comment_run(GDbComment *comment, GLoadedBinary *binary, bool ap
             if (apply)
             {
                 comment->old_count = scount;
-                comment->old_inlined = (GLineGenerator **)realloc(comment->old_inlined,
-                                                                  comment->old_count * sizeof(GLineGenerator *));
+                comment->old_inlined = realloc(comment->old_inlined, comment->old_count * sizeof(GLineGenerator *));
             }
 
             for (i = 0; i < scount && result; i++)
@@ -805,7 +804,7 @@ static bool g_db_comment_run(GDbComment *comment, GLoadedBinary *binary, bool ap
         assert(comment->old_count == 0);
 
         comment->old_count = 1;
-        comment->oldies = (GDbComment **)calloc(comment->old_count, sizeof(GDbComment *));
+        comment->oldies = calloc(comment->old_count, sizeof(GDbComment *));
 
     }
 
@@ -876,8 +875,7 @@ static bool g_db_comment_run(GDbComment *comment, GLoadedBinary *binary, bool ap
             if (apply)
             {
                 comment->old_count += scount;
-                comment->oldies = (GDbComment **)realloc(comment->oldies,
-                                                         comment->old_count * sizeof(GDbComment *));
+                comment->oldies = realloc(comment->oldies, comment->old_count * sizeof(GDbComment *));
             }
 
             for (i = 0; i < scount && result; i++)
@@ -970,73 +968,6 @@ static bool g_db_comment_cancel(GDbComment *comment, GLoadedBinary *binary)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : comment = base d'éléments sur laquelle s'appuyer.            *
-*                values   = couples de champs et de valeurs à lier. [OUT]     *
-*                count    = nombre de ces couples. [OUT]                      *
-*                                                                             *
-*  Description : Constitue les champs destinés à une insertion / modification.*
-*                                                                             *
-*  Retour      : Etat du besoin en sauvegarde.                                *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static bool g_db_comment_prepare_db_statement(GDbComment *comment, bound_value **values, size_t *count)
-{
-    bool status;                            /* Bilan d'opération initiale  */
-    bound_value *value;                     /* Valeur à éditer / définir   */
-    rle_string text;                        /* Texte brut récupéré         */
-
-    status = G_DB_ITEM_CLASS(g_db_comment_parent_class)->prepare_stmt(G_DB_ITEM(comment), values, count);
-    if (!status) return false;
-
-    status = prepare_vmpa_db_statement(&comment->addr, NULL, values, count);
-    if (!status) return false;
-
-    *count += 1;
-    *values = (bound_value *)realloc(*values, *count * sizeof(bound_value));
-
-    value = &(*values)[*count - 1];
-
-    value->cname = "flags";
-    value->built_name = false;
-    value->type = SQLITE_INTEGER;
-    value->integer = comment->flags;
-    value->delete = NULL;
-
-    init_dynamic_rle_string(&text, g_db_comment_get_text(comment));
-    status &= prepare_db_statement_for_rle_string(&text, "text", values, count);
-    exit_rle_string(&text);
-
-    if (!status) return false;
-
-    *count += 2;
-    *values = (bound_value *)realloc(*values, *count * sizeof(bound_value));
-
-    value = &(*values)[*count - 2];
-
-    value->cname = "inlined";
-    value->built_name = false;
-    value->type = SQLITE_BOOLEAN;
-    value->boolean = comment->inlined;
-    value->delete = NULL;
-
-    value = &(*values)[*count - 1];
-
-    value->cname = "repeatable";
-    value->built_name = false;
-    value->type = SQLITE_BOOLEAN;
-    value->boolean = comment->repeatable;
-    value->delete = NULL;
-
-    return true;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
 *  Paramètres  : comment = commentaire textuel à charger depuis les réponses. *
 *                values  = tableau d'éléments à consulter.                    *
 *                count   = nombre de descriptions renseignées.                *
@@ -1101,6 +1032,98 @@ static bool g_db_comment_load(GDbComment *comment, const bound_value *values, si
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : comment = base d'éléments sur laquelle s'appuyer.            *
+*                values   = couples de champs et de valeurs à lier. [OUT]     *
+*                count    = nombre de ces couples. [OUT]                      *
+*                                                                             *
+*  Description : Constitue les champs destinés à une insertion / modification.*
+*                                                                             *
+*  Retour      : Etat du besoin en sauvegarde.                                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_db_comment_store(GDbComment *comment, bound_value **values, size_t *count)
+{
+    bool status;                            /* Bilan d'opération initiale  */
+    bound_value *value;                     /* Valeur à éditer / définir   */
+    rle_string text;                        /* Texte brut récupéré         */
+
+    if (comment == NULL)
+        status = G_DB_ITEM_CLASS(g_db_comment_parent_class)->store(NULL, values, count);
+    else
+        status = G_DB_ITEM_CLASS(g_db_comment_parent_class)->store(G_DB_ITEM(comment), values, count);
+
+    if (!status) return false;
+
+    if (comment == NULL)
+        status = store_vmpa(NULL, NULL, values, count);
+    else
+        status = store_vmpa(&comment->addr, NULL, values, count);
+
+    if (!status) return false;
+
+    *count += 1;
+    *values = realloc(*values, *count * sizeof(bound_value));
+
+    value = &(*values)[*count - 1];
+
+    value->cname = "flags";
+    value->built_name = false;
+    value->type = SQLITE_INTEGER;
+    value->integer = comment->flags;
+    value->delete = NULL;
+
+    value->has_value = (comment != NULL);
+
+    if (value->has_value)
+    {
+        init_dynamic_rle_string(&text, g_db_comment_get_text(comment));
+        status = store_rle_string(&text, "text", values, count);
+        exit_rle_string(&text);
+    }
+
+    if (!status) return false;
+
+    *count += 2;
+    *values = realloc(*values, *count * sizeof(bound_value));
+
+    value = &(*values)[*count - 2];
+
+    value->cname = "inlined";
+    value->built_name = false;
+    value->type = SQLITE_BOOLEAN;
+
+    value->has_value = (comment != NULL);
+
+    if (value->has_value)
+    {
+        value->boolean = comment->inlined;
+        value->delete = NULL;
+    }
+
+    value = &(*values)[*count - 1];
+
+    value->cname = "repeatable";
+    value->built_name = false;
+    value->type = SQLITE_BOOLEAN;
+
+    value->has_value = (comment != NULL);
+
+    if (value->has_value)
+    {
+        value->boolean = comment->repeatable;
+        value->delete = NULL;
+    }
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : comment = informations à consulter.                          *
 *                                                                             *
 *  Description : Fournit l'adresse associée à un commentaire.                 *
@@ -1649,11 +1672,11 @@ static bool g_comment_collection_setup_load(GCommentCollection *collec, bound_va
                                                                                   values, count);
     if (!status) return false;
 
-    if (!setup_load_for_vmpa(NULL, values, count))
+    if (!store_vmpa(NULL, NULL, values, count))
         return false;
 
     *count += 1;
-    *values = (bound_value *)realloc(*values, *count * sizeof(bound_value));
+    *values = realloc(*values, *count * sizeof(bound_value));
 
     value = &(*values)[*count - 1];
 
@@ -1661,11 +1684,11 @@ static bool g_comment_collection_setup_load(GCommentCollection *collec, bound_va
     value->built_name = false;
     value->type = SQLITE_INTEGER;
 
-    if (!setup_load_of_rle_string(NULL, "text", values, count))
+    if (!store_rle_string(NULL, "text", values, count))
         return false;
 
     *count += 2;
-    *values = (bound_value *)realloc(*values, *count * sizeof(bound_value));
+    *values = realloc(*values, *count * sizeof(bound_value));
 
     value = &(*values)[*count - 2];
 
diff --git a/src/analysis/db/items/move.c b/src/analysis/db/items/move.c
index 6929ec3..286215b 100644
--- a/src/analysis/db/items/move.c
+++ b/src/analysis/db/items/move.c
@@ -95,12 +95,12 @@ static bool g_db_move_apply(GDbMove *, GLoadedBinary *);
 /* Annule un déplacement au sein d'une zone de code. */
 static bool g_db_move_cancel(GDbMove *, GLoadedBinary *);
 
-/* Constitue les champs destinés à une insertion / modification. */
-static bool g_db_move_prepare_db_statement(const GDbMove *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour un déplacement dans du code. */
 static bool g_db_move_load(GDbMove *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+static bool g_db_move_store(const GDbMove *, bound_value **, size_t *);
+
 
 
 /* ---------------------- DEFINITION DE LA COLLECTION ASSOCIEE ---------------------- */
@@ -188,8 +188,8 @@ static void g_db_move_class_init(GDbMoveClass *klass)
     item->apply = (run_item_fc)g_db_move_apply;
     item->cancel = (run_item_fc)g_db_move_cancel;
 
-    item->prepare_stmt = (prepare_db_statement)g_db_move_prepare_db_statement;
     item->load = (load_db_item_fc)g_db_move_load;
+    item->store = (store_db_item_fc)g_db_move_store;
 
 }
 
@@ -455,7 +455,7 @@ static bool g_db_move_run(const GDbMove *move, GLineCursor *cursor)
 
     if (G_IS_LOADED_PANEL(panel))
     {
-        params = (move_params *)calloc(1, sizeof(move_params));
+        params = calloc(1, sizeof(move_params));
 
         params->panel = panel;
         params->cursor = cursor;
@@ -527,63 +527,75 @@ static bool g_db_move_cancel(GDbMove *move, GLoadedBinary *binary)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : move   = base d'éléments sur laquelle s'appuyer.             *
-*                values = couples de champs et de valeurs à lier. [OUT]       *
-*                count  = nombre de ces couples. [OUT]                        *
+*  Paramètres  : move   = bascule d'affichage à charger depuis les réponses.  *
+*                values = tableau d'éléments à consulter.                     *
+*                count  = nombre de descriptions renseignées.                 *
 *                                                                             *
-*  Description : Constitue les champs destinés à une insertion / modification.*
+*  Description : Charge les valeurs utiles pour un déplacement dans du code.  *
 *                                                                             *
-*  Retour      : Etat du besoin en sauvegarde.                                *
+*  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool g_db_move_prepare_db_statement(const GDbMove *move, bound_value **values, size_t *count)
+static bool g_db_move_load(GDbMove *move, const bound_value *values, size_t count)
 {
-    bool status;                            /* Bilan d'opération initiale  */
+    bool result;                            /* Bilan à faire remonter      */
 
-    status = G_DB_ITEM_CLASS(g_db_move_parent_class)->prepare_stmt(G_DB_ITEM(move), values, count);
-    if (!status) return false;
+    result = G_DB_ITEM_CLASS(g_db_move_parent_class)->load(G_DB_ITEM(move), values, count);
 
-    status = g_line_cursor_prepare_db_statement(move->src, "src", values, count);
-    if (!status) return false;
+    if (result)
+        result = g_line_cursor_load(move->src, "src", values, count);
 
-    status = g_line_cursor_prepare_db_statement(move->src, "dest", values, count);
-    if (!status) return false;
+    if (result)
+        result = g_line_cursor_load(move->dest, "dest", values, count);
 
-    return true;
+    return result;
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : move   = bascule d'affichage à charger depuis les réponses.  *
-*                values = tableau d'éléments à consulter.                     *
-*                count  = nombre de descriptions renseignées.                 *
+*  Paramètres  : move   = base d'éléments sur laquelle s'appuyer.             *
+*                values = couples de champs et de valeurs à lier. [OUT]       *
+*                count  = nombre de ces couples. [OUT]                        *
 *                                                                             *
-*  Description : Charge les valeurs utiles pour un déplacement dans du code.  *
+*  Description : Constitue les champs destinés à une insertion / modification.*
 *                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
+*  Retour      : Etat du besoin en sauvegarde.                                *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool g_db_move_load(GDbMove *move, const bound_value *values, size_t count)
+static bool g_db_move_store(const GDbMove *move, bound_value **values, size_t *count)
 {
-    bool result;                            /* Bilan à faire remonter      */
+    bool status;                            /* Bilan d'opération initiale  */
 
-    result = G_DB_ITEM_CLASS(g_db_move_parent_class)->load(G_DB_ITEM(move), values, count);
+    if (move == NULL)
+        status = G_DB_ITEM_CLASS(g_db_move_parent_class)->store(NULL, values, count);
+    else
+        status = G_DB_ITEM_CLASS(g_db_move_parent_class)->store(G_DB_ITEM(move), values, count);
 
-    if (result)
-        result = g_line_cursor_load(move->src, "src", values, count);
+    if (!status) return false;
 
-    if (result)
-        result = g_line_cursor_load(move->dest, "dest", values, count);
+    if (move == NULL)
+        status = g_line_cursor_setup_load(move->src, "src", values, count);
+    else
+        status = g_line_cursor_store(move->src, "src", values, count);
 
-    return result;
+    if (!status) return false;
+
+    if (move == NULL)
+        status = g_line_cursor_setup_load(move->dest, "dest", values, count);
+    else
+        status = g_line_cursor_store(move->dest, "dest", values, count);
+
+    if (!status) return false;
+
+    return true;
 
 }
 
@@ -783,10 +795,10 @@ static bool g_move_collection_setup_load(GMoveCollection *collec, bound_value **
                                                                                    values, count);
     if (!status) return false;
 
-    if (!g_binary_cursor_setup_load("src", values, count))
+    if (!g_binary_cursor_store(NULL, "src", values, count))
         return false;
 
-    if (!g_binary_cursor_setup_load("dest", values, count))
+    if (!g_binary_cursor_store(NULL, "dest", values, count))
         return false;
 
     return true;
diff --git a/src/analysis/db/items/switcher.c b/src/analysis/db/items/switcher.c
index b4f0379..e086b14 100644
--- a/src/analysis/db/items/switcher.c
+++ b/src/analysis/db/items/switcher.c
@@ -95,12 +95,11 @@ static bool g_db_switcher_apply(GDbSwitcher *, GLoadedBinary *);
 /* Annule une bascule d'affichage d'opérande sur un binaire. */
 static bool g_db_switcher_cancel(GDbSwitcher *, GLoadedBinary *);
 
-/* Constitue les champs destinés à une insertion / modification. */
-static bool g_db_switcher_prepare_db_statement(const GDbSwitcher *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour un basculement d'affichage. */
 static bool g_db_switcher_load(GDbSwitcher *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+static bool g_db_switcher_store(const GDbSwitcher *, bound_value **, size_t *);
 
 
 
@@ -189,8 +188,8 @@ static void g_db_switcher_class_init(GDbSwitcherClass *klass)
     item->apply = (run_item_fc)g_db_switcher_apply;
     item->cancel = (run_item_fc)g_db_switcher_cancel;
 
-    item->prepare_stmt = (prepare_db_statement)g_db_switcher_prepare_db_statement;
     item->load = (load_db_item_fc)g_db_switcher_load;
+    item->store = (store_db_item_fc)g_db_switcher_store;
 
 }
 
@@ -638,55 +637,6 @@ static bool g_db_switcher_cancel(GDbSwitcher *switcher, GLoadedBinary *binary)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : switcher = base d'éléments sur laquelle s'appuyer.           *
-*                values   = couples de champs et de valeurs à lier. [OUT]     *
-*                count    = nombre de ces couples. [OUT]                      *
-*                                                                             *
-*  Description : Constitue les champs destinés à une insertion / modification.*
-*                                                                             *
-*  Retour      : Etat du besoin en sauvegarde.                                *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static bool g_db_switcher_prepare_db_statement(const GDbSwitcher *switcher, bound_value **values, size_t *count)
-{
-    bool status;                            /* Bilan d'opération initiale  */
-    bound_value *value;                     /* Valeur à éditer / définir   */
-
-    status = G_DB_ITEM_CLASS(g_db_switcher_parent_class)->prepare_stmt(G_DB_ITEM(switcher), values, count);
-    if (!status) return false;
-
-    status = prepare_vmpa_db_statement(&switcher->addr, NULL, values, count);
-    if (!status) return false;
-
-    *count += 2;
-    *values = (bound_value *)realloc(*values, *count * sizeof(bound_value));
-
-    value = &(*values)[*count - 2];
-
-    value->cname = "op_index";
-    value->built_name = false;
-    value->type = SQLITE_INTEGER;
-    value->integer = switcher->index;
-    value->delete = NULL;
-
-    value = &(*values)[*count - 1];
-
-    value->cname = "type";
-    value->built_name = false;
-    value->type = SQLITE_INTEGER;
-    value->integer = switcher->display;
-    value->delete = NULL;
-
-    return true;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
 *  Paramètres  : switcher = bascule d'affichage à charger depuis les réponses.*
 *                values   = tableau d'éléments à consulter.                   *
 *                count    = nombre de descriptions renseignées.               *
@@ -733,6 +683,75 @@ static bool g_db_switcher_load(GDbSwitcher *switcher, const bound_value *values,
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : switcher = base d'éléments sur laquelle s'appuyer.           *
+*                values   = couples de champs et de valeurs à lier. [OUT]     *
+*                count    = nombre de ces couples. [OUT]                      *
+*                                                                             *
+*  Description : Constitue les champs destinés à une insertion / modification.*
+*                                                                             *
+*  Retour      : Etat du besoin en sauvegarde.                                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_db_switcher_store(const GDbSwitcher *switcher, bound_value **values, size_t *count)
+{
+    bool status;                            /* Bilan d'opération initiale  */
+    bound_value *value;                     /* Valeur à éditer / définir   */
+
+    if (switcher == NULL)
+        status = G_DB_ITEM_CLASS(g_db_switcher_parent_class)->store(NULL, values, count);
+    else
+        status = G_DB_ITEM_CLASS(g_db_switcher_parent_class)->store(G_DB_ITEM(switcher), values, count);
+
+    if (!status) return false;
+
+    if (switcher == NULL)
+        status = store_vmpa(NULL, NULL, values, count);
+    else
+        status = store_vmpa(&switcher->addr, NULL, values, count);
+
+    if (!status) return false;
+
+    *count += 2;
+    *values = realloc(*values, *count * sizeof(bound_value));
+
+    value = &(*values)[*count - 2];
+
+    value->cname = "op_index";
+    value->built_name = false;
+    value->type = SQLITE_INTEGER;
+
+    value->has_value = (switcher != NULL);
+
+    if (value->has_value)
+    {
+        value->integer = switcher->index;
+        value->delete = NULL;
+    }
+
+    value = &(*values)[*count - 1];
+
+    value->cname = "type";
+    value->built_name = false;
+    value->type = SQLITE_INTEGER;
+
+    value->has_value = (switcher != NULL);
+
+    if (value->has_value)
+    {
+        value->integer = switcher->display;
+        value->delete = NULL;
+    }
+
+    return true;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                        DEFINITION DE LA COLLECTION ASSOCIEE                        */
@@ -927,11 +946,11 @@ static bool g_switcher_collection_setup_load(GSwitcherCollection *collec, bound_
                                                                                    values, count);
     if (!status) return false;
 
-    if (!setup_load_for_vmpa(NULL, values, count))
+    if (!store_vmpa(NULL, NULL, values, count))
         return false;
 
     *count += 2;
-    *values = (bound_value *)realloc(*values, *count * sizeof(bound_value));
+    *values = realloc(*values, *count * sizeof(bound_value));
 
     value = &(*values)[*count - 2];
 
diff --git a/src/analysis/db/misc/rlestr.c b/src/analysis/db/misc/rlestr.c
index 196b934..f26c146 100644
--- a/src/analysis/db/misc/rlestr.c
+++ b/src/analysis/db/misc/rlestr.c
@@ -326,31 +326,42 @@ bool pack_rle_string(const rle_string *str, packed_buffer *pbuf)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : str    = chaîne de caractères aux informations inutiles.     *
+*  Paramètres  : str    = chaîne de caractères à compléter.                   *
 *                name   = désignation personnalisée du champ dans la BD.      *
-*                values = couples de champs et de valeurs à lier. [OUT]       *
-*                count  = nombre de ces couples. [OUT]                        *
+*                values = tableau d'éléments à consulter.                     *
+*                count  = nombre de descriptions renseignées.                 *
 *                                                                             *
-*  Description : Constitue les champs destinés à une insertion / modification.*
+*  Description : Charge les valeurs utiles pour une chaîne de caractères.     *
 *                                                                             *
-*  Retour      : Bilan de l'opération : succès ou non.                        *
+*  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-bool prepare_db_statement_for_rle_string(const rle_string *str, const char *name, bound_value **values, size_t *count)
+bool load_rle_string(rle_string *str, const char *name, const bound_value *values, size_t count)
 {
-    bound_value *value;                     /* Valeur à éditer / définir   */
+    const bound_value *value;               /* Valeur à intégrer           */
 
-    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
-    value = &(*values)[*count - 1];
+    value = find_bound_value(values, count, name);
+    if (value == NULL) return false;
 
-    value->cname = name;
-    value->built_name = false;
-    value->type = (get_rle_string(str) != NULL ? SQLITE_TEXT : SQLITE_NULL);
-    value->cstring = get_rle_string(str);
-    value->delete = SQLITE_STATIC;
+    switch (value->type)
+    {
+        case SQLITE_TEXT:
+            unset_rle_string(str);
+            dup_into_rle_string(str, value->cstring);
+            break;
+
+        case SQLITE_NULL:
+            unset_rle_string(str);
+            break;
+
+        default:
+            return false;
+            break;
+
+    }
 
     return true;
 
@@ -361,71 +372,39 @@ bool prepare_db_statement_for_rle_string(const rle_string *str, const char *name
 *                                                                             *
 *  Paramètres  : str    = chaîne de caractères aux informations inutiles.     *
 *                name   = désignation personnalisée du champ dans la BD.      *
-*                values = tableau d'éléments à compléter. [OUT]               *
-*                count  = nombre de descriptions renseignées. [OUT]           *
+*                values = couples de champs et de valeurs à lier. [OUT]       *
+*                count  = nombre de ces couples. [OUT]                        *
 *                                                                             *
-*  Description : Décrit les colonnes utiles à une chaîne de caractères.       *
+*  Description : Constitue les champs destinés à une insertion / modification.*
 *                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
+*  Retour      : Bilan de l'opération : succès ou non.                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-bool setup_load_of_rle_string(const rle_string *str, const char *name, bound_value **values, size_t *count)
+bool store_rle_string(const rle_string *str, const char *name, bound_value **values, size_t *count)
 {
     bound_value *value;                     /* Valeur à éditer / définir   */
 
-    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+    *values = realloc(*values, ++(*count) * sizeof(bound_value));
+
     value = &(*values)[*count - 1];
 
     value->cname = name;
     value->built_name = false;
-    value->type = SQLITE_NATIVE;
 
-    return true;
+    value->has_value = (str != NULL);
 
+    if (value->has_value)
+        value->type = (get_rle_string(str) != NULL ? SQLITE_TEXT : SQLITE_NULL);
+    else
+        value->type = SQLITE_NATIVE;
 
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : str    = chaîne de caractères à compléter.                   *
-*                name   = désignation personnalisée du champ dans la BD.      *
-*                values = tableau d'éléments à consulter.                     *
-*                count  = nombre de descriptions renseignées.                 *
-*                                                                             *
-*  Description : Charge les valeurs utiles pour une chaîne de caractères.     *
-*                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-bool load_rle_string(rle_string *str, const char *name, const bound_value *values, size_t count)
-{
-    const bound_value *value;               /* Valeur à intégrer           */
-
-    value = find_bound_value(values, count, name);
-    if (value == NULL) return false;
-
-    switch (value->type)
+    if (value->has_value)
     {
-        case SQLITE_TEXT:
-            unset_rle_string(str);
-            dup_into_rle_string(str, value->cstring);
-            break;
-
-        case SQLITE_NULL:
-            unset_rle_string(str);
-            break;
-
-        default:
-            return false;
-            break;
-
+        value->cstring = get_rle_string(str);
+        value->delete = SQLITE_STATIC;
     }
 
     return true;
diff --git a/src/analysis/db/misc/rlestr.h b/src/analysis/db/misc/rlestr.h
index 7b317b6..eb71582 100644
--- a/src/analysis/db/misc/rlestr.h
+++ b/src/analysis/db/misc/rlestr.h
@@ -94,15 +94,12 @@ bool pack_rle_string(const rle_string *, packed_buffer *);
 #define SQLITE_RLESTR_CREATE(n)     \
     n " TEXT"
 
-/* Constitue les champs destinés à une insertion / modification. */
-bool prepare_db_statement_for_rle_string(const rle_string *, const char *, bound_value **, size_t *);
-
-/* Décrit les colonnes utiles à une chaîne de caractères. */
-bool setup_load_of_rle_string(const rle_string *, const char *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour une chaîne de caractères. */
 bool load_rle_string(rle_string *, const char *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+bool store_rle_string(const rle_string *, const char *, bound_value **, size_t *);
+
 
 
 #endif  /* _ANALYSIS_DB_MISC_RLESTR_H */
diff --git a/src/analysis/db/misc/timestamp.c b/src/analysis/db/misc/timestamp.c
index 5302c60..1d2fcd9 100644
--- a/src/analysis/db/misc/timestamp.c
+++ b/src/analysis/db/misc/timestamp.c
@@ -166,30 +166,28 @@ bool pack_timestamp(const timestamp_t *timestamp, packed_buffer *pbuf)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : timestamp = horodatage aux informations inutiles.            *
+*  Paramètres  : timestamp = horodatage à compléter.                          *
 *                name      = désignation personnalisée du champ dans la BD.   *
-*                values    = couples de champs et de valeurs à lier. [OUT]    *
-*                count     = nombre de ces couples. [OUT]                     *
+*                values    = tableau d'éléments à consulter.                  *
+*                count     = nombre de descriptions renseignées.              *
 *                                                                             *
-*  Description : Constitue les champs destinés à une insertion / modification.*
+*  Description : Charge les valeurs utiles pour un horodatage.                *
 *                                                                             *
-*  Retour      : Bilan de l'opération : succès ou non.                        *
+*  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-bool prepare_db_statement_for_timestamp(const timestamp_t *timestamp, const char *name, bound_value **values, size_t *count)
+bool load_timestamp(timestamp_t *timestamp, const char *name, const bound_value *values, size_t count)
 {
-    bound_value *value;                     /* Valeur à éditer / définir   */
+    const bound_value *value;               /* Valeur à intégrer           */
 
-    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
-    value = &(*values)[*count - 1];
+    value = find_bound_value(values, count, name);
+    if (value == NULL) return false;
+    if (value->type != SQLITE_INT64) return false;
 
-    value->cname = name;
-    value->built_name = false;
-    value->type = SQLITE_INT64;
-    value->integer64 = *timestamp;
+    *timestamp = value->integer64;
 
     return true;
 
@@ -200,58 +198,33 @@ bool prepare_db_statement_for_timestamp(const timestamp_t *timestamp, const char
 *                                                                             *
 *  Paramètres  : timestamp = horodatage aux informations inutiles.            *
 *                name      = désignation personnalisée du champ dans la BD.   *
-*                values    = tableau d'éléments à compléter. [OUT]            *
-*                count     = nombre de descriptions renseignées. [OUT]        *
+*                values    = couples de champs et de valeurs à lier. [OUT]    *
+*                count     = nombre de ces couples. [OUT]                     *
 *                                                                             *
-*  Description : Décrit les colonnes utiles à un horodatage.                  *
+*  Description : Constitue les champs destinés à une insertion / modification.*
 *                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
+*  Retour      : Bilan de l'opération : succès ou non.                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-bool setup_load_of_timestamp(const timestamp_t *timestamp, const char *name, bound_value **values, size_t *count)
+bool store_timestamp(const timestamp_t *timestamp, const char *name, bound_value **values, size_t *count)
 {
     bound_value *value;                     /* Valeur à éditer / définir   */
 
-    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+    *values = realloc(*values, ++(*count) * sizeof(bound_value));
+
     value = &(*values)[*count - 1];
 
     value->cname = name;
     value->built_name = false;
     value->type = SQLITE_INT64;
 
-    return true;
-
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : timestamp = horodatage à compléter.                          *
-*                name      = désignation personnalisée du champ dans la BD.   *
-*                values    = tableau d'éléments à consulter.                  *
-*                count     = nombre de descriptions renseignées.              *
-*                                                                             *
-*  Description : Charge les valeurs utiles pour un horodatage.                *
-*                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-bool load_timestamp(timestamp_t *timestamp, const char *name, const bound_value *values, size_t count)
-{
-    const bound_value *value;               /* Valeur à intégrer           */
-
-    value = find_bound_value(values, count, name);
-    if (value == NULL) return false;
-    if (value->type != SQLITE_INT64) return false;
+    value->has_value = (timestamp != NULL);
 
-    *timestamp = value->integer64;
+    if (value->has_value)
+        value->integer64 = *timestamp;
 
     return true;
 
diff --git a/src/analysis/db/misc/timestamp.h b/src/analysis/db/misc/timestamp.h
index 076a4e0..eba0fa7 100644
--- a/src/analysis/db/misc/timestamp.h
+++ b/src/analysis/db/misc/timestamp.h
@@ -67,15 +67,12 @@ bool pack_timestamp(const timestamp_t *, packed_buffer *);
 #define SQLITE_TIMESTAMP_CREATE(n)      \
     n " INTEGER"
 
-/* Constitue les champs destinés à une insertion / modification. */
-bool prepare_db_statement_for_timestamp(const timestamp_t *, const char *, bound_value **, size_t *);
-
-/* Décrit les colonnes utiles à un horodatage. */
-bool setup_load_of_timestamp(const timestamp_t *, const char *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour un horodatage. */
 bool load_timestamp(timestamp_t *, const char *, const bound_value *, size_t);
 
+/* Constitue les champs destinés à une insertion / modification. */
+bool store_timestamp(const timestamp_t *, const char *, bound_value **, size_t *);
+
 
 
 #endif  /* _ANALYSIS_DB_MISC_TIMESTAMP_H */
diff --git a/src/arch/vmpa.c b/src/arch/vmpa.c
index 0c0511b..a2a3ba5 100644
--- a/src/arch/vmpa.c
+++ b/src/arch/vmpa.c
@@ -715,44 +715,6 @@ char *create_vmpa_db_table(const char *base)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : base   = tronc commun pour les champs de la base de données. *
-*                values = tableau d'éléments à compléter. [OUT]               *
-*                count  = nombre de descriptions renseignées. [OUT]           *
-*                                                                             *
-*  Description : Décrit les colonnes utiles à un chargement de données.       *
-*                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-bool setup_load_for_vmpa(const char *base, bound_value **values, size_t *count)
-{
-    bound_value *value;                     /* Valeur à éditer / définir   */
-
-    (*count) += 2;
-    *values = (bound_value *)realloc(*values, (*count) * sizeof(bound_value));
-
-    value = &(*values)[*count - 2];
-
-    asprintf(&value->name, "%s%sphys", base != NULL ? base : "", base != NULL ? "_" : "");
-    value->built_name = true;
-    value->type = SQLITE_NATIVE;
-
-    value = &(*values)[*count - 1];
-
-    asprintf(&value->name, "%s%svirt", base != NULL ? base : "", base != NULL ? "_" : "");
-    value->built_name = true;
-    value->type = SQLITE_NATIVE;
-
-    return true;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
 *  Paramètres  : addr   = localisation dont la définition est à définir.      *
 *                base   = tronc commun pour les champs de la base de données. *
 *                values = tableau d'éléments à consulter.                     *
@@ -839,40 +801,47 @@ bool load_vmpa(vmpa2t *addr, const char *base, const bound_value *values, size_t
 *                                                                             *
 ******************************************************************************/
 
-bool prepare_vmpa_db_statement(const vmpa2t *addr, const char *base, bound_value **values, size_t *count)
+bool store_vmpa(const vmpa2t *addr, const char *base, bound_value **values, size_t *count)
 {
     bound_value *value;                     /* Valeur à éditer / définir   */
 
-    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
-    value = &(*values)[*count - 1];
+    *count += 2;
+    *values = realloc(*values, *count * sizeof(bound_value));
+
+    value = &(*values)[*count - 2];
 
     asprintf(&value->name, "%s%sphys", base != NULL ? base : "", base != NULL ? "_" : "");
     value->built_name = true;
 
-    if (addr->physical != VMPA_NO_PHYSICAL)
-    {
-        value->type = SQLITE_INT64;
-        value->integer64 = addr->physical;
-    }
-    else
+    value->has_value = (addr != NULL);
+
+    if (value->has_value)
     {
-        value->type = SQLITE_NULL;
+        if (addr->physical != VMPA_NO_PHYSICAL)
+        {
+            value->type = SQLITE_INT64;
+            value->integer64 = addr->physical;
+        }
+        else
+            value->type = SQLITE_NULL;
     }
 
-    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
     value = &(*values)[*count - 1];
 
     asprintf(&value->name, "%s%svirt", base != NULL ? base : "", base != NULL ? "_" : "");
     value->built_name = true;
 
-    if (addr->virtual != VMPA_NO_VIRTUAL)
-    {
-        value->type = SQLITE_INT64;
-        value->integer64 = addr->virtual;
-    }
-    else
+    value->has_value = (addr != NULL);
+
+    if (value->has_value)
     {
-        value->type = SQLITE_NULL;
+        if (addr->virtual != VMPA_NO_VIRTUAL)
+        {
+            value->type = SQLITE_INT64;
+            value->integer64 = addr->virtual;
+        }
+        else
+            value->type = SQLITE_NULL;
     }
 
     return true;
diff --git a/src/arch/vmpa.h b/src/arch/vmpa.h
index fc997e5..8dfc646 100644
--- a/src/arch/vmpa.h
+++ b/src/arch/vmpa.h
@@ -145,14 +145,11 @@ vmpa2t *string_to_vmpa_virt(const char *);
 /* Donne les éléments requis pour la construction d'une table. */
 char *create_vmpa_db_table(const char *);
 
-/* Décrit les colonnes utiles à un chargement de données. */
-bool setup_load_for_vmpa(const char *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour une localisation. */
 bool load_vmpa(vmpa2t *, const char *, const bound_value *, size_t);
 
 /* Constitue les champs destinés à une insertion / modification. */
-bool prepare_vmpa_db_statement(const vmpa2t *, const char *, bound_value **, size_t *);
+bool store_vmpa(const vmpa2t *, const char *, bound_value **, size_t *);
 
 
 
diff --git a/src/common/sqlite.c b/src/common/sqlite.c
index a3b97b0..af2af9f 100644
--- a/src/common/sqlite.c
+++ b/src/common/sqlite.c
@@ -24,10 +24,19 @@
 #include "sqlite.h"
 
 
+#include <assert.h>
 #include <malloc.h>
 #include <string.h>
 
 
+#include "extstr.h"
+
+
+
+/* Attribue une définition aux valeurs paramétrées. */
+static bool bind_bound_values(sqlite3 *, sqlite3_stmt *, const char *, const bound_value *, size_t, bool, int *);
+
+
 
 /******************************************************************************
 *                                                                             *
@@ -92,3 +101,376 @@ const bound_value *find_bound_value(const bound_value *values, size_t count, con
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : db     = base de données à consulter.                        *
+*                stmt      = requête SQL en préparation à faire évoluer.      *
+*                sql       = définition brute de cette requête SQL.           *
+*                values    = tableau d'éléments à consulter.                  *
+*                count     = nombre de descriptions renseignées.              *
+*                has_value = fitre sur la définition des valeurs.             *
+*                index     = indice évolutif des valeurs paramétrées. [OUT]   *
+*                                                                             *
+*  Description : Attribue une définition aux valeurs paramétrées.             *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool bind_bound_values(sqlite3 *db, sqlite3_stmt *stmt, const char *sql, const bound_value *values, size_t count, bool has_value, int *index)
+{
+    bool result;                            /* Bilan à retourner           */
+    size_t i;                               /* Boucle de parcours          */
+    int ret;                                /* Bilan d'un appel à SQLite   */
+
+    result = true;
+
+    for (i = 0; i < count && result; i++)
+    {
+        if (values[i].has_value != has_value)
+            continue;
+
+        switch (values[i].type)
+        {
+            case SQLITE_BOOLEAN:
+                ret = sqlite3_bind_int(stmt, *index, values[i].boolean);
+                break;
+
+            case SQLITE_INTEGER:
+                ret = sqlite3_bind_int(stmt, *index, values[i].integer);
+                break;
+
+            case SQLITE_INT64:
+                ret = sqlite3_bind_int64(stmt, *index, values[i].integer64);
+                break;
+
+            case SQLITE_TEXT:
+                ret = sqlite3_bind_text(stmt, *index, values[i].string, -1, values[i].delete);
+                break;
+
+            case SQLITE_NULL:
+                ret = sqlite3_bind_null(stmt, *index);
+                break;
+
+            default:
+                assert(false);
+                ret = SQLITE_ERROR;
+                break;
+
+        }
+
+        if (ret == SQLITE_OK)
+            (*index)++;
+
+        else
+        {
+            result = false;
+
+            fprintf(stderr, "Can't bind value for parameter nb %d in '%s' (ret=%d): %s\n",
+                    *index, sql, ret, sqlite3_errmsg(db));
+
+        }
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : db     = base de données à consulter.                        *
+*                table  = nom de la table concernée.                          *
+*                values = champs avec leur valeur.                            *
+*                count  = quantité de ces champs.                             *
+*                cb     = procédure à appeler pour chaque nouvelle série.     *
+*                data   = éventuelles données associées à transmettre.        *
+*                                                                             *
+*  Description : Charge une série de valeurs depuis une base de données.      *
+*                                                                             *
+*  Retour      : Bilan de l'exécution de l'opération.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool load_db_values(sqlite3 *db, const char *table, bound_value *values, size_t count, db_load_cb cb, void *data)
+{
+    bool result;                            /* Conclusion à faire remonter */
+    char *sql;                              /* Requête SQL à construire    */
+    bool first;                             /* Marque du premier passage   */
+    size_t i;                               /* Boucle de parcours          */
+    sqlite3_stmt *stmt;                     /* Déclaration mise en place   */
+    int ret;                                /* Bilan d'un appel à SQLite   */
+    int index;                              /* Indice de valeur attachée   */
+    int native_type;                        /* Type de valeur dans la base */
+
+    result = false;
+
+    /* Préparation de la requête */
+
+    sql = strdup("SELECT ");
+
+    first = true;
+
+    for (i = 0; i < count; i++)
+    {
+        if (values[i].has_value)
+            continue;
+
+        if (!first) sql = stradd(sql, ", ");
+
+        sql = stradd(sql, values[i].name);
+
+        first = false;
+
+    }
+
+    assert(!first);
+
+    sql = stradd(sql, " FROM ");
+    sql = stradd(sql, table);
+
+    first = true;
+
+    for (i = 0; i < count; i++)
+    {
+        if (!values[i].has_value)
+            continue;
+
+        if (first)
+            sql = stradd(sql, " WHERE ");
+        else
+            sql = stradd(sql, " AND ");
+
+        sql = stradd(sql, values[i].name);
+
+        sql = stradd(sql, " = ?");
+
+        first = false;
+
+    }
+
+    sql = stradd(sql, ";");
+
+    ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+	if (ret != SQLITE_OK)
+    {
+		fprintf(stderr, "Can't prepare SELECT statment '%s' (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
+        goto prepare_error;
+	}
+
+    /* Attribution des valeurs */
+
+    index = 1;
+
+    if (!bind_bound_values(db, stmt, sql, values, count, true, &index))
+        goto bind_error;
+
+    /* Chargement des valeurs existantes */
+
+    result = true;
+
+    for (ret = sqlite3_step(stmt); ret == SQLITE_ROW && result; ret = sqlite3_step(stmt))
+    {
+        /* Conversion des valeurs */
+
+        index = 0;
+
+        for (i = 0; i < count; i++)
+        {
+            if (values[i].has_value)
+                continue;
+
+            native_type = sqlite3_column_type(stmt, index);
+
+            /**
+             * On réalise une petite conversion selon le champ.
+             *
+             * Le filtre SQLITE_NATIVE est destiné à conserver le type choisi par
+             * SQLite. Typiquement, une chaîne peut être à SQLITE_NULL ou SQLITE_TEXT
+             * selon la valeur conservée dans la base.
+             *
+             * D'autres éléments, comme les localisations en mémoire, peuvent aussi
+             * avoir un champ éventuellement nul, donc la définition à partir des
+             * indications de la base de données reste importante.
+             *
+             * En ce qui concerne les valeurs numériques, SQLite ne fait pas de
+             * distinction : tout passe par la fonction sqlite3VdbeIntValue(),
+             * qui effectue des transtypages au besoin pour tout ce qui n'est
+             * pas numérique.
+             *
+             * Pour les types internes SQLITE_INTEGER et SQLITE_BOOLEAN,
+             * il est donc nécessaire d'ajuster en interne.
+             */
+
+            if (native_type == SQLITE_INTEGER)
+                native_type = SQLITE_INT64;
+
+            if (values[i].type == SQLITE_NATIVE)
+                values[i].type = native_type;
+
+            else
+                assert(values[i].type == native_type
+                       || values[i].type == SQLITE_INTEGER
+                       || values[i].type == SQLITE_BOOLEAN);
+
+            switch (values[i].type)
+            {
+                case SQLITE_BOOLEAN:
+                    values[i].boolean = (bool)sqlite3_column_int(stmt, index);
+                    break;
+
+                case SQLITE_INTEGER:
+                    values[i].integer = sqlite3_column_int(stmt, index);
+                    break;
+
+                case SQLITE_INT64:
+                    values[i].integer64 = sqlite3_column_int64(stmt, index);
+                    break;
+
+                case SQLITE_FLOAT:
+                    assert(0);
+                    break;
+
+                case SQLITE_TEXT:
+                    values[i].cstring = (const char *)sqlite3_column_text(stmt, index);
+                    break;
+
+                case SQLITE_BLOB:
+                    assert(0);
+                    break;
+
+                case SQLITE_NULL:
+                    break;
+
+                default:
+                    assert(0);
+                    break;
+
+            }
+
+        }
+
+        /* Chargement d'un nouvel élément */
+
+        cb(values, count, data);
+
+        index++;
+
+    }
+
+ bind_error:
+
+    sqlite3_finalize(stmt);
+
+ prepare_error:
+
+    free(sql);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : db     = base de données à mettre à jour.                    *
+*                table  = nom de la table concernée.                          *
+*                values = champs avec leur valeur.                            *
+*                count  = quantité de ces champs.                             *
+*                                                                             *
+*  Description : Enregistre une série de valeurs dans une base de données.    *
+*                                                                             *
+*  Retour      : Bilan de l'exécution de l'opération.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool store_db_values(sqlite3 *db, const char *table, const bound_value *values, size_t count)
+{
+    bool result;                            /* Conclusion à faire remonter */
+    char *sql;                              /* Requête SQL à construire    */
+    size_t i;                               /* Boucle de parcours          */
+    sqlite3_stmt *stmt;                     /* Déclaration mise en place   */
+    int ret;                                /* Bilan d'un appel à SQLite   */
+    int index;                              /* Indice de valeur attachée   */
+
+    result = false;
+
+    /* Préparation de la requête */
+
+    sql = strdup("INSERT INTO ");
+    sql = stradd(sql, table);
+    sql = stradd(sql, " (");
+
+    for (i = 0; i < count; i++)
+    {
+        assert(values[i].has_value);
+
+        if (i > 0) sql = stradd(sql, ", ");
+
+        sql = stradd(sql, values[i].name);
+
+    }
+
+    sql = stradd(sql, ") VALUES (");
+
+    for (i = 0; i < count; i++)
+    {
+        if (i > 0) sql = stradd(sql, ", ");
+
+        if (values[i].type == SQLITE_RAW)
+            sql = stradd(sql, values[i].cstring);
+        else
+            sql = stradd(sql, "?");
+
+    }
+
+    sql = stradd(sql, ");");
+
+	ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+	if (ret != SQLITE_OK)
+    {
+		fprintf(stderr, "Can't prepare INSERT statment '%s' (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
+        goto prepare_error;
+	}
+
+    /* Attribution des valeurs */
+
+    index = 1;
+
+    if (!bind_bound_values(db, stmt, sql, values, count, true, &index))
+        goto bind_error;
+
+    /* Exécution finale */
+
+	ret = sqlite3_step(stmt);
+
+    if (ret != SQLITE_DONE)
+    {
+		fprintf(stderr, "INSERT statement '%s' didn't return DONE (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
+        goto insert_error;
+    }
+
+    result = true;
+
+ insert_error:
+
+ bind_error:
+
+    sqlite3_finalize(stmt);
+
+ prepare_error:
+
+    free(sql);
+
+    return result;
+
+}
diff --git a/src/common/sqlite.h b/src/common/sqlite.h
index 1d31746..291eec8 100644
--- a/src/common/sqlite.h
+++ b/src/common/sqlite.h
@@ -25,6 +25,7 @@
 #define _COMMON_SQLITE_H
 
 
+#include <sqlite3.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <sys/types.h>
@@ -51,6 +52,8 @@ typedef struct _bound_value
 
     unsigned int type;                      /* Type de valeur à associer   */
 
+    bool has_value;                         /* Validité des champs suivants*/
+
     union
     {
         bool boolean;                       /* Etat sur 1 bit              */
@@ -72,6 +75,15 @@ void free_all_bound_values(bound_value *, size_t);
 /* Effectue une recherche au sein d'un ensemble de valeurs. */
 const bound_value *find_bound_value(const bound_value *, size_t, const char *);
 
+/* Interagit avec des valeurs chargées. */
+typedef bool (* db_load_cb) (const bound_value *, size_t, void *);
+
+/* Charge une série de valeurs depuis une base de données. */
+bool load_db_values(sqlite3 *, const char *, bound_value *, size_t, db_load_cb, void *);
+
+/* Enregistre une série de valeurs dans une base de données. */
+bool store_db_values(sqlite3 *, const char *, const bound_value *, size_t);
+
 
 
 #endif  /* _COMMON_SQLITE_H */
diff --git a/src/glibext/gbinarycursor.c b/src/glibext/gbinarycursor.c
index ee9eb5f..9c4e82b 100644
--- a/src/glibext/gbinarycursor.c
+++ b/src/glibext/gbinarycursor.c
@@ -97,9 +97,6 @@ static bool g_binary_cursor_unserialize(GBinaryCursor *, packed_buffer *);
 /* Charge les valeurs utiles pour une localisation. */
 static bool g_binary_cursor_load(GBinaryCursor *, const char *, const bound_value *, size_t);
 
-/* Constitue les champs destinés à une insertion / modification. */
-static bool g_binary_cursor_prepare_db_statement(const GBinaryCursor *, const char *, bound_value **, size_t *);
-
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -145,9 +142,8 @@ static void g_binary_cursor_class_init(GBinaryCursorClass *class)
     line->unserialize = (unserialize_cursor_fc)g_binary_cursor_unserialize;
 
     line->create_db = (create_cursor_db_table_fc)g_binary_cursor_create_db_table;
-    line->setup_load = (setup_cursor_load_fc)g_binary_cursor_setup_load;
     line->load = (load_cursor_fc)g_binary_cursor_load;
-    line->prepare = (prepare_cursor_db_statement_fc)g_binary_cursor_prepare_db_statement;
+    line->store = (store_cursor_fc)g_binary_cursor_store;
 
 }
 
@@ -500,31 +496,6 @@ char *g_binary_cursor_create_db_table(const char *base)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : base   = tronc commun pour les champs de la base de données. *
-*                values = tableau d'éléments à compléter. [OUT]               *
-*                count  = nombre de descriptions renseignées. [OUT]           *
-*                                                                             *
-*  Description : Décrit les colonnes utiles à un chargement de données.       *
-*                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-bool g_binary_cursor_setup_load(const char *base, bound_value **values, size_t *count)
-{
-    bool result;                            /* Bilan à renvoyer            */
-
-    result = setup_load_for_vmpa(base, values, count);
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
 *  Paramètres  : cursor = suivi de positions dont la définition est à définir.*
 *                base   = tronc commun pour les champs de la base de données. *
 *                values = tableau d'éléments à consulter.                     *
@@ -564,11 +535,14 @@ static bool g_binary_cursor_load(GBinaryCursor *cursor, const char *base, const
 *                                                                             *
 ******************************************************************************/
 
-static bool g_binary_cursor_prepare_db_statement(const GBinaryCursor *cursor, const char *base, bound_value **values, size_t *count)
+bool g_binary_cursor_store(const GBinaryCursor *cursor, const char *base, bound_value **values, size_t *count)
 {
     bool result;                            /* Bilan à renvoyer            */
 
-    result = prepare_vmpa_db_statement(&cursor->addr, base, values, count);
+    if (cursor == NULL)
+        result = store_vmpa(NULL, base, values, count);
+    else
+        result = store_vmpa(&cursor->addr, base, values, count);
 
     return result;
 
diff --git a/src/glibext/gbinarycursor.h b/src/glibext/gbinarycursor.h
index 63a6f1b..ffac19a 100644
--- a/src/glibext/gbinarycursor.h
+++ b/src/glibext/gbinarycursor.h
@@ -71,8 +71,8 @@ void g_binary_cursor_get_info(const GBinaryCursor *, vmpa2t *);
 /* Donne les éléments requis pour la construction d'une table. */
 char *g_binary_cursor_create_db_table(const char *);
 
-/* Décrit les colonnes utiles à un chargement de données. */
-bool g_binary_cursor_setup_load(const char *, bound_value **, size_t *);
+/* Constitue les champs destinés à une insertion / modification. */
+bool g_binary_cursor_store(const GBinaryCursor *, const char *, bound_value **, size_t *);
 
 
 
diff --git a/src/glibext/glinecursor-int.h b/src/glibext/glinecursor-int.h
index 18ec147..b6a0f19 100644
--- a/src/glibext/glinecursor-int.h
+++ b/src/glibext/glinecursor-int.h
@@ -54,14 +54,11 @@ typedef bool (* unserialize_cursor_fc) (GLineCursor *, packed_buffer *);
 /* Donne les éléments requis pour la construction d'une table. */
 typedef char *(* create_cursor_db_table_fc) (const char *);
 
-/* Décrit les colonnes utiles à un chargement de données. */
-typedef bool (* setup_cursor_load_fc) (const char *, bound_value **, size_t *);
-
 /* Charge les valeurs utiles pour une localisation. */
 typedef bool (* load_cursor_fc) (GLineCursor *, const char *, const bound_value *, size_t);
 
 /* Constitue les champs destinés à une insertion / modification. */
-typedef bool (* prepare_cursor_db_statement_fc) (const GLineCursor *, const char *, bound_value **, size_t *);
+typedef bool (* store_cursor_fc) (const GLineCursor *, const char *, bound_value **, size_t *);
 
 
 /* Suivi de positions dans un panneau de chargement (instance) */
@@ -86,9 +83,8 @@ struct _GLineCursorClass
     unserialize_cursor_fc unserialize;      /* Chargement d'un emplacement */
 
     create_cursor_db_table_fc create_db;    /* Création de table           */
-    setup_cursor_load_fc setup_load;        /* Préparation du chargement   */
     load_cursor_fc load;                    /* Chargement de valeurs       */
-    prepare_cursor_db_statement_fc prepare; /* Préparation d'enregistrement*/
+    store_cursor_fc store;                  /* Préparation d'enregistrement*/
 
 };
 
diff --git a/src/glibext/glinecursor.c b/src/glibext/glinecursor.c
index 64521f4..bef9cb6 100644
--- a/src/glibext/glinecursor.c
+++ b/src/glibext/glinecursor.c
@@ -356,7 +356,7 @@ bool g_line_cursor_setup_load(const GLineCursor *cursor, const char *base, bound
 {
     bool result;                            /* Bilan à renvoyer            */
 
-    result = G_LINE_CURSOR_GET_CLASS(cursor)->setup_load(base, values, count);
+    result = G_LINE_CURSOR_GET_CLASS(cursor)->store(NULL, base, values, count);
 
     return result;
 
@@ -404,11 +404,11 @@ bool g_line_cursor_load(GLineCursor *cursor, const char *base, const bound_value
 *                                                                             *
 ******************************************************************************/
 
-bool g_line_cursor_prepare_db_statement(const GLineCursor *cursor, const char *base, bound_value **values, size_t *count)
+bool g_line_cursor_store(const GLineCursor *cursor, const char *base, bound_value **values, size_t *count)
 {
     bool result;                            /* Bilan à renvoyer            */
 
-    result = G_LINE_CURSOR_GET_CLASS(cursor)->prepare(cursor, base, values, count);
+    result = G_LINE_CURSOR_GET_CLASS(cursor)->store(cursor, base, values, count);
 
     return result;
 
diff --git a/src/glibext/glinecursor.h b/src/glibext/glinecursor.h
index 4fa93c1..c373562 100644
--- a/src/glibext/glinecursor.h
+++ b/src/glibext/glinecursor.h
@@ -102,7 +102,7 @@ bool g_line_cursor_setup_load(const GLineCursor *, const char *, bound_value **,
 bool g_line_cursor_load(GLineCursor *, const char *, const bound_value *, size_t);
 
 /* Constitue les champs destinés à une insertion / modification. */
-bool g_line_cursor_prepare_db_statement(const GLineCursor *, const char *, bound_value **, size_t *);
+bool g_line_cursor_store(const GLineCursor *, const char *, bound_value **, size_t *);
 
 
 
-- 
cgit v0.11.2-87-g4458