rough framework
[lemu] / db.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <assert.h>
4
5 #include <sqlite3.h>
6
7 #include "common.h"
8 #include "notify.h"
9 #include "db.h"
10
11 static const char db_engine_[] = "sqlite3";
12
13 struct prepared_statement {
14 const char *statement_name;
15 const char *arguments;
16 const char *query;
17 sqlite3_stmt *statement;
18 } prepared_statements[] = {
19 { "begin_transaction", "", "BEGIN TRANSACTION", NULL },
20 { "end_transaction", "", "END TRANSACTION", NULL },
21 { "vacuum", "", "VACUUM", NULL },
22 { "uid_from_name", "s", "SELECT uid FROM uid_name WHERE name = ?", NULL },
23 { NULL, NULL, NULL, NULL }
24 };
25
26 enum prepared_statement_enums {
27 PS_BEGIN,
28 PS_END,
29 PS_VACUUM,
30 PS_UID_FROM_NAME,
31 _NUM_PS_ENUMS
32 };
33
34 /**
35 * pre-prepare all queries used
36 */
37 static int
38 db_init_all_statements_(sqlite3 *db)
39 {
40 struct prepared_statement *s;
41 int r;
42
43 for (s = prepared_statements; s->statement_name; s++) {
44 r = sqlite3_prepare_v2(db, s->query, -1, &s->statement, NULL);
45 if (r != SQLITE_OK) {
46 NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_prepare_v2", s->statement_name, r, sqlite3_errmsg(db));
47 return -1;
48 }
49 }
50
51 return 0;
52 }
53
54 /**
55 * finalize all queries used
56 */
57 static void
58 db_finalize_all_statements_(sqlite3 *db)
59 {
60 struct prepared_statement *s;
61 int r;
62
63 for (s = prepared_statements; s->statement_name; s++) {
64 if (s->statement) {
65 r = sqlite3_finalize(s->statement);
66 if (r != SQLITE_OK) {
67 NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_finalize", s->statement_name, r, sqlite3_errmsg(db));
68 }
69 s->statement = NULL;
70 }
71 }
72 }
73
74 const char *
75 db_engine()
76 {
77 return db_engine_;
78 }
79
80 const char *
81 db_version()
82 {
83 return sqlite3_libversion();
84 }
85
86
87 /**
88 *
89 */
90 int
91 db_init(struct database *db, struct database_options *db_opts)
92 {
93 int r;
94
95 /* Ensure sqlite header matches library. */
96 assert(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER);
97 assert(strncmp(sqlite3_sourceid(), SQLITE_SOURCE_ID, 80) == 0);
98 assert(strcmp(sqlite3_libversion(), SQLITE_VERSION) == 0);
99
100 r = sqlite3_open_v2(db_opts->db_file, &db->db, SQLITE_OPEN_READWRITE, NULL);
101 if (r != SQLITE_OK) {
102 NOTIFY_ERROR("error opening %s '%s': %s", "db_file", db_opts->db_file, sqlite3_errmsg(db->db));
103 return -1;
104 }
105
106 if (db_init_all_statements_(db->db)) {
107 NOTIFY_ERROR("%s:%s", "db_init_all_statements", "failed");
108 return -1;
109 }
110
111 return 0;
112 }
113
114 /**
115 *
116 */
117 void
118 db_fini(struct database *db)
119 {
120 int r;
121
122 db_finalize_all_statements_(db->db);
123 r = sqlite3_close(db->db);
124 if (r != SQLITE_OK) {
125 NOTIFY_ERROR("%s:%s", "sqlite3_close", sqlite3_errmsg(db->db));
126 }
127 db->db = NULL;
128 }
129
130 /**
131 * given a string, returns the uid which matches
132 */
133 int
134 db_uid_from_name(struct database *db, unsigned char *name, int *uid)
135 {
136 struct prepared_statement *ps = &prepared_statements[PS_UID_FROM_NAME];
137 int retval = -1;
138 int r;
139
140 r = sqlite3_bind_text(ps->statement, 1, (char *)name, -1, SQLITE_TRANSIENT);
141 if (r != SQLITE_OK) {
142 NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_bind_text", name, r, sqlite3_errmsg(db->db));
143 return -2;
144 }
145
146 do {
147 r = sqlite3_step(ps->statement);
148 if (r == SQLITE_ROW) {
149 if (retval == 0) {
150 NOTIFY_ERROR("%s:%s", "internal db error", "multiple uids for name");
151 }
152 *uid = sqlite3_column_int(ps->statement, 0);
153 retval = 0;
154
155 if (*uid == 0) {
156 if (SQLITE_INTEGER != sqlite3_column_type(ps->statement, 0)) {
157 NOTIFY_ERROR("%s:%s", "internal db error", "uid is not an integer");
158 retval = -3;
159 }
160 }
161 }
162 } while (r == SQLITE_ROW || r == SQLITE_BUSY);
163
164 if (r != SQLITE_DONE) {
165 NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_step", name, r, sqlite3_errmsg(db->db));
166 retval = -3;
167 }
168
169 r = sqlite3_reset(ps->statement);
170 if (r != SQLITE_OK) {
171 NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_reset", name, r, sqlite3_errmsg(db->db));
172 }
173 r = sqlite3_clear_bindings(ps->statement);
174 if (r != SQLITE_OK) {
175 NOTIFY_ERROR("%s('%s'):%d:%s", "sqlite3_clear_bindings", name, r, sqlite3_errmsg(db->db));
176 }
177
178 return retval;
179 }