summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2019-05-08 09:28:58 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2019-05-08 09:28:58 (GMT)
commit86ba53836168bcc591f532f2419fa290de601572 (patch)
tree91c96614d6f7e3fd75800a52c0166c91c7f8ef31 /src/common
parent28e53c2498903090182ebeb128347fcd92896cd9 (diff)
Updated the core functions dealing with SQLite databases.
Diffstat (limited to 'src/common')
-rw-r--r--src/common/sqlite.c382
-rw-r--r--src/common/sqlite.h12
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 */