Line data Source code
1 : #include <string.h>
2 : #include <stdlib.h>
3 : #include <unistd.h>
4 : #include <errno.h>
5 :
6 : #include "config.h"
7 : #include "swap-table.h"
8 :
9 : #if HAVE_SQLITE3
10 :
11 : #define SET_ERROR(...) g_set_error(error, RM_ERROR_QUARK, 0, __VA_ARGS__);
12 :
13 : /////////////////////
14 : // GENERAL TYPES //
15 : /////////////////////
16 :
17 : enum { STMT_CREATE_ATTR, STMT_INSERT_DATA, STMT_SELECT_DATA, N_STMTS };
18 :
19 : static const char *STATEMENTS[] =
20 : {[STMT_CREATE_ATTR] = "CREATE TABLE %s_vec (%s BLOB NOT NULL);",
21 : [STMT_INSERT_DATA] = "INSERT INTO %s_vec VALUES(?); -- %s",
22 : [STMT_SELECT_DATA] = "SELECT %s FROM %s_vec WHERE rowid = ?;", [N_STMTS] = NULL};
23 :
24 : typedef struct RmSwapAttr {
25 : int id;
26 : const char *name;
27 : sqlite3_stmt *stmts[N_STMTS];
28 : } RmSwapAttr;
29 :
30 : /////////////////////////
31 : // UTILITY FUNCTIONS //
32 : /////////////////////////
33 :
34 2622 : static RmSwapAttr *rm_swap_attr_create(sqlite3 *handle, int id, const char *name,
35 : GError **error) {
36 2622 : g_assert(name);
37 :
38 2622 : RmSwapAttr *self = g_malloc0(sizeof(RmSwapAttr));
39 2622 : self->name = name;
40 2622 : self->id = id;
41 :
42 10488 : for(int idx = 0; idx < N_STMTS; ++idx) {
43 7866 : char *dynamic_sql = sqlite3_mprintf(STATEMENTS[idx], name, name);
44 :
45 7866 : if(sqlite3_prepare_v2(handle, dynamic_sql, -1, &self->stmts[idx], NULL) !=
46 : SQLITE_OK) {
47 0 : SET_ERROR("Unable to prepare statement");
48 : }
49 :
50 7866 : if(idx == STMT_CREATE_ATTR) {
51 2622 : sqlite3_stmt *stmt = self->stmts[idx];
52 2622 : if(sqlite3_step(stmt) != SQLITE_DONE) {
53 0 : SET_ERROR("Unable to create attr table");
54 : }
55 :
56 2622 : if(sqlite3_reset(stmt) != SQLITE_OK) {
57 0 : SET_ERROR("Unable to reset attr attr table stmt");
58 : }
59 : }
60 :
61 7866 : if(dynamic_sql != NULL) {
62 7866 : sqlite3_free(dynamic_sql);
63 : }
64 : }
65 :
66 2622 : return self;
67 : }
68 :
69 2622 : static void rm_swap_attr_destroy(RmSwapAttr *self, RmSwapTable *table) {
70 2622 : g_assert(self);
71 2622 : g_assert(table);
72 :
73 10488 : for(int idx = 0; idx < N_STMTS; ++idx) {
74 7866 : sqlite3_stmt *stmt = self->stmts[idx];
75 7866 : sqlite3_finalize(stmt);
76 : }
77 :
78 2622 : g_free(self);
79 2622 : }
80 :
81 70099 : static void rm_swap_table_clean_stmt(RmSwapTable *self, sqlite3_stmt *stmt,
82 : GError **error) {
83 70099 : g_assert(self);
84 70099 : g_assert(stmt);
85 :
86 70099 : if(sqlite3_errcode(self->cache) != SQLITE_DONE) {
87 0 : SET_ERROR("stmt failed: %s", sqlite3_errmsg(self->cache));
88 : }
89 :
90 70099 : if(sqlite3_reset(stmt) != SQLITE_OK) {
91 0 : SET_ERROR("Unable to reset prepared statement");
92 : }
93 :
94 70099 : if(sqlite3_clear_bindings(stmt) != SQLITE_OK) {
95 0 : SET_ERROR("Unable to clear prepared statement");
96 : }
97 70099 : }
98 :
99 1311 : static void rm_swap_table_create_cachedir(GError **error) {
100 1311 : char *path = g_build_filename(g_get_user_cache_dir(), "rmlint", NULL);
101 :
102 1311 : if(g_mkdir_with_parents(path, 0775) == -1) {
103 0 : SET_ERROR("cannot create cache dir %s: %s", path, g_strerror(errno));
104 : }
105 :
106 1311 : g_free(path);
107 1311 : }
108 :
109 : //////////////////////
110 : // SWAP TABLE API //
111 : //////////////////////
112 :
113 1311 : RmSwapTable *rm_swap_table_open(gboolean in_memory, GError **error) {
114 1311 : RmSwapTable *self = NULL;
115 :
116 1311 : char *path = NULL;
117 :
118 1311 : if(in_memory) {
119 0 : path = g_strdup(":memory:");
120 : } else {
121 1311 : char pid[20] = {0};
122 1311 : memset(pid, 0, sizeof(pid));
123 :
124 1311 : g_snprintf(pid, sizeof(pid), "%d", getpid());
125 1311 : path = g_build_filename(g_get_user_cache_dir(), "rmlint", pid, NULL);
126 :
127 : /* Make sure that path actually exists */
128 1311 : rm_swap_table_create_cachedir(error);
129 : }
130 :
131 : /* Might happen if no tmp file could be created */
132 1311 : if(error && *error) {
133 0 : goto cleanup;
134 : }
135 :
136 : /* We do locking ourselves */
137 1311 : sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
138 :
139 1311 : sqlite3 *handle = NULL;
140 1311 : if(sqlite3_open(path, &handle) != SQLITE_OK) {
141 0 : SET_ERROR("Cannot open swap table db");
142 0 : goto cleanup;
143 : }
144 :
145 : /* Finetune sqlite (quite slow without these) */
146 1311 : sqlite3_extended_result_codes(handle, TRUE);
147 1311 : sqlite3_exec(handle, "PRAGMA cache_size = 8000;", 0, 0, 0);
148 1311 : sqlite3_exec(handle, "PRAGMA synchronous = OFF;", 0, 0, 0);
149 1311 : sqlite3_exec(handle, "PRAGMA journal_mode = MEMORY;", 0, 0, 0);
150 :
151 : #if !RM_IS_APPLE
152 1311 : sqlite3_enable_shared_cache(TRUE);
153 : #endif
154 :
155 1311 : size_t path_len = strlen(path) + 1;
156 1311 : self = g_malloc(sizeof(RmSwapTable) + path_len);
157 1311 : self->cache = handle;
158 1311 : self->attrs = g_ptr_array_new();
159 1311 : self->transaction_running = FALSE;
160 1311 : strncpy(self->path, path, path_len);
161 1311 : g_mutex_init(&self->mtx);
162 :
163 : cleanup:
164 1311 : g_free(path);
165 1311 : return self;
166 : }
167 :
168 1311 : void rm_swap_table_close(RmSwapTable *self, GError **error) {
169 1311 : g_assert(self);
170 :
171 1311 : g_mutex_lock(&self->mtx);
172 : {
173 1311 : g_ptr_array_foreach(self->attrs, (GFunc)rm_swap_attr_destroy, self);
174 1311 : g_ptr_array_free(self->attrs, TRUE);
175 :
176 1311 : if(sqlite3_close(self->cache) != SQLITE_OK) {
177 0 : SET_ERROR("Unable to close swap table db");
178 : }
179 :
180 1311 : if(unlink(self->path) == -1) {
181 0 : SET_ERROR("cannot delete temp cache %s: %s", self->path, g_strerror(errno));
182 : }
183 : }
184 1311 : g_mutex_unlock(&self->mtx);
185 1311 : g_mutex_clear(&self->mtx);
186 1311 : self->cache = NULL;
187 1311 : self->attrs = NULL;
188 1311 : g_free(self);
189 1311 : }
190 :
191 2622 : int rm_swap_table_create_attr(RmSwapTable *self, const char *name, GError **error) {
192 2622 : g_assert(self);
193 :
194 2622 : RmSwapAttr *attribute = NULL;
195 2622 : g_mutex_lock(&self->mtx);
196 : {
197 2622 : attribute = rm_swap_attr_create(self->cache, self->attrs->len, name, error);
198 2622 : g_ptr_array_add(self->attrs, attribute);
199 : }
200 2622 : g_mutex_unlock(&self->mtx);
201 :
202 : /* Create RmSwapAttr, return id, add it to table, create table. */
203 2622 : return (attribute) ? attribute->id : 0;
204 : }
205 :
206 4944 : static void rm_swap_table_begin(RmSwapTable *self) {
207 4944 : g_assert(self);
208 :
209 4944 : if(sqlite3_exec(self->cache, "BEGIN IMMEDIATE;", 0, 0, 0) == SQLITE_OK) {
210 4944 : self->transaction_running = TRUE;
211 : }
212 4944 : }
213 :
214 4944 : static void rm_swap_table_commit(RmSwapTable *self) {
215 4944 : g_assert(self);
216 :
217 4944 : if(sqlite3_exec(self->cache, "COMMIT;", 0, 0, 0) == SQLITE_OK) {
218 4944 : self->transaction_running = FALSE;
219 : }
220 4944 : }
221 :
222 58413 : size_t rm_swap_table_lookup(RmSwapTable *self, int attr, RmOff id, char *buf,
223 : size_t buf_size) {
224 58413 : g_assert(self);
225 :
226 58413 : size_t bytes_written = 0;
227 :
228 58413 : g_mutex_lock(&self->mtx);
229 : {
230 : /* Commit any stalled transactions */
231 58417 : if(self->transaction_running) {
232 4944 : rm_swap_table_commit(self);
233 : }
234 :
235 58417 : RmSwapAttr *attribute = self->attrs->pdata[attr];
236 58417 : sqlite3_stmt *stmt = attribute->stmts[STMT_SELECT_DATA];
237 :
238 58417 : if(sqlite3_bind_int64(stmt, 1, id) != SQLITE_OK) {
239 0 : g_assert_not_reached();
240 : }
241 :
242 58417 : if(sqlite3_step(stmt) == SQLITE_ROW) {
243 58417 : bytes_written = MIN((size_t)sqlite3_column_bytes(stmt, 0), buf_size);
244 58417 : memcpy(buf, sqlite3_column_blob(stmt, 0), bytes_written);
245 : }
246 :
247 58417 : sqlite3_step(stmt); /* == SQLITE_DONE */
248 :
249 58417 : rm_swap_table_clean_stmt(self, stmt, NULL);
250 : }
251 58417 : g_mutex_unlock(&self->mtx);
252 :
253 58417 : return bytes_written;
254 : }
255 :
256 11682 : RmOff rm_swap_table_insert(RmSwapTable *self, int attr, char *data, size_t data_len) {
257 11682 : g_assert(self);
258 :
259 11682 : RmOff id = 0;
260 :
261 11682 : g_mutex_lock(&self->mtx);
262 : {
263 : /* Begin an transaction if none is running yet */
264 11682 : if(self->transaction_running == FALSE) {
265 4944 : rm_swap_table_begin(self);
266 : }
267 :
268 11682 : RmSwapAttr *attribute = self->attrs->pdata[attr];
269 11682 : sqlite3_stmt *stmt = attribute->stmts[STMT_INSERT_DATA];
270 :
271 11682 : if(sqlite3_bind_text(stmt, 1, data, data_len, NULL) != SQLITE_OK) {
272 0 : g_assert_not_reached();
273 : }
274 :
275 11682 : if(sqlite3_step(stmt) == SQLITE_DONE) {
276 11682 : id = sqlite3_last_insert_rowid(self->cache);
277 : }
278 :
279 11682 : rm_swap_table_clean_stmt(self, stmt, NULL);
280 : }
281 11682 : g_mutex_unlock(&self->mtx);
282 :
283 11682 : return id;
284 : }
285 :
286 : #else /* HAVE_SQLITE3 */
287 :
288 : RmSwapTable *rm_swap_table_open(_U gboolean in_memory, _U GError **error) {
289 : return NULL;
290 : }
291 :
292 : void rm_swap_table_close(_U RmSwapTable *self, _U GError **error) {
293 : return;
294 : }
295 :
296 : int rm_swap_table_create_attr(_U RmSwapTable *self, _U const char *name,
297 : _U GError **error) {
298 : return 0;
299 : }
300 :
301 : RmOff rm_swap_table_insert(_U RmSwapTable *self, _U int attr, _U char *data,
302 : _U size_t data_len) {
303 : return 0;
304 : }
305 :
306 : size_t rm_swap_table_lookup(_U RmSwapTable *self, _U int attr, _U RmOff id, _U char *buf,
307 : _U size_t buf_size) {
308 : return 0;
309 : }
310 :
311 : #endif
312 :
313 : //////////////////////
314 : // UGLY TEST MAIN //
315 : //////////////////////
316 :
317 : // #define _RM_COMPILE_SWAP_TABLE_MAIN
318 : #ifdef _RM_COMPILE_SWAP_TABLE_MAIN
319 :
320 : int main(void) {
321 : GError *error = NULL;
322 : RmSwapTable *table = rm_swap_table_open(FALSE, &error);
323 :
324 : if(error != NULL) {
325 : return EXIT_FAILURE;
326 : }
327 :
328 : int PATH_ATTR = rm_swap_table_create_attr(table, "path", &error);
329 :
330 : const int N = 1000000;
331 : const int PATH_LEN = 80; /* Typical average path len */
332 :
333 : for(int i = 0; i < (N); i++) {
334 : char buf[PATH_LEN + 1];
335 : memset(buf, (i % ('~' - '!')) + '!', PATH_LEN);
336 :
337 : buf[PATH_LEN] = 0;
338 :
339 : rm_swap_table_insert(table, PATH_ATTR, buf, sizeof(buf));
340 : }
341 :
342 : for(int i = 0; i < (N); i++) {
343 : char buf[PATH_MAX];
344 : rm_swap_table_lookup(table, PATH_ATTR, i + 1, buf, sizeof(buf));
345 : }
346 :
347 : rm_swap_table_close(table, &error);
348 :
349 : if(error != NULL) {
350 : g_error_free(error);
351 : }
352 :
353 : return EXIT_SUCCESS;
354 : }
355 :
356 : #endif
|