--- /dev/null
+#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;
+}