rough framework
[lemu] / db.c
diff --git a/db.c b/db.c
new file mode 100644 (file)
index 0000000..d089231
--- /dev/null
+++ b/db.c
@@ -0,0 +1,179 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sqlite3.h>
+
+#include "common.h"
+#include "notify.h"
+#include "db.h"
+
+static const char db_engine_[] = "sqlite3";
+
+struct prepared_statement {
+       const char *statement_name;
+       const char *arguments;
+       const char *query;
+       sqlite3_stmt *statement;
+} prepared_statements[] = {
+       { "begin_transaction", "", "BEGIN TRANSACTION", NULL },
+       { "end_transaction", "", "END TRANSACTION", NULL },
+       { "vacuum", "", "VACUUM", NULL },
+       { "uid_from_name", "s", "SELECT uid FROM uid_name WHERE name = ?", NULL },
+       { NULL, NULL, NULL, NULL }
+};
+
+enum prepared_statement_enums {
+       PS_BEGIN,
+       PS_END,
+       PS_VACUUM,
+       PS_UID_FROM_NAME,
+       _NUM_PS_ENUMS
+};
+
+/**
+ *  pre-prepare all queries used
+ */
+static int
+db_init_all_statements_(sqlite3 *db)
+{
+       struct prepared_statement *s;
+       int r;
+
+       for (s = prepared_statements; s->statement_name; s++) {
+               r = sqlite3_prepare_v2(db, s->query, -1, &s->statement, NULL);
+               if (r != SQLITE_OK) {
+                       NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_prepare_v2", s->statement_name, r, sqlite3_errmsg(db));
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ *  finalize all queries used
+ */
+static void
+db_finalize_all_statements_(sqlite3 *db)
+{
+       struct prepared_statement *s;
+       int r;
+
+       for (s = prepared_statements; s->statement_name; s++) {
+               if (s->statement) {
+                       r = sqlite3_finalize(s->statement);
+                       if (r != SQLITE_OK) {
+                               NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_finalize", s->statement_name, r, sqlite3_errmsg(db));
+                       }
+                       s->statement = NULL;
+               }
+       }
+}
+
+const char *
+db_engine()
+{
+       return db_engine_;
+}
+
+const char *
+db_version()
+{
+       return sqlite3_libversion();
+}
+
+
+/**
+ * 
+ */
+int
+db_init(struct database *db, struct database_options *db_opts)
+{
+       int r;
+
+       /* Ensure sqlite header matches library. */
+       assert(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER);
+       assert(strncmp(sqlite3_sourceid(), SQLITE_SOURCE_ID, 80) == 0);
+       assert(strcmp(sqlite3_libversion(), SQLITE_VERSION) == 0);
+
+       r = sqlite3_open_v2(db_opts->db_file, &db->db, SQLITE_OPEN_READWRITE, NULL);
+       if (r != SQLITE_OK) {
+               NOTIFY_ERROR("error opening %s '%s': %s", "db_file", db_opts->db_file, sqlite3_errmsg(db->db));
+               return -1;
+       }
+
+       if (db_init_all_statements_(db->db)) {
+               NOTIFY_ERROR("%s:%s", "db_init_all_statements", "failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * 
+ */
+void
+db_fini(struct database *db)
+{
+       int r;
+
+       db_finalize_all_statements_(db->db);
+       r = sqlite3_close(db->db);
+       if (r != SQLITE_OK) {
+               NOTIFY_ERROR("%s:%s", "sqlite3_close", sqlite3_errmsg(db->db));
+       }
+       db->db = NULL;
+}
+
+/** 
+ *  given a string, returns the uid which matches
+ */
+int
+db_uid_from_name(struct database *db, unsigned char *name, int *uid)
+{
+       struct prepared_statement *ps = &prepared_statements[PS_UID_FROM_NAME];
+       int retval = -1;
+       int r;
+
+       r = sqlite3_bind_text(ps->statement, 1, (char *)name, -1, SQLITE_TRANSIENT);
+       if (r != SQLITE_OK) {
+               NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_bind_text", name, r, sqlite3_errmsg(db->db));
+               return -2;
+       }
+
+       do {
+               r = sqlite3_step(ps->statement);
+               if (r == SQLITE_ROW) {
+                       if (retval == 0) {
+                               NOTIFY_ERROR("%s:%s", "internal db error", "multiple uids for name");
+                       }
+                       *uid = sqlite3_column_int(ps->statement, 0);
+                       retval = 0;
+
+                       if (*uid == 0) {
+                               if (SQLITE_INTEGER != sqlite3_column_type(ps->statement, 0)) {
+                                       NOTIFY_ERROR("%s:%s", "internal db error", "uid is not an integer");
+                                       retval = -3;
+                               }
+                       }
+               }
+       } while (r == SQLITE_ROW || r == SQLITE_BUSY);
+
+       if (r != SQLITE_DONE) {
+               NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_step", name, r, sqlite3_errmsg(db->db));
+               retval = -3;
+       }
+
+       r = sqlite3_reset(ps->statement);
+       if (r != SQLITE_OK) {
+               NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_reset", name, r, sqlite3_errmsg(db->db));
+       }
+       r = sqlite3_clear_bindings(ps->statement);
+       if (r != SQLITE_OK) {
+               NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_clear_bindings", name, r, sqlite3_errmsg(db->db));
+       }
+
+       return retval;
+}