#include #include #include #include #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; }