diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/sqlite.c | 382 | ||||
-rw-r--r-- | src/common/sqlite.h | 12 |
2 files changed, 394 insertions, 0 deletions
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 */ |