2 This file is part of GNUnet
3 (C) 2006, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file datacache/plugin_datacache_sqlite.c
23 * @brief sqlite for an implementation of a database backend for the datacache
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_datacache_plugin.h"
31 #define DEBUG_DATACACHE_SQLITE GNUNET_NO
34 * How much overhead do we assume per entry in the
37 #define OVERHEAD (sizeof(GNUNET_HashCode) + 32)
40 * Context for all functions in this plugin.
45 * Our execution environment.
47 struct GNUNET_DATACACHE_PluginEnvironment *env;
50 * Handle to the sqlite database.
55 * Filename used for the DB.
62 * Log an error message at log-level 'level' that indicates
63 * a failure of the command 'cmd' on file 'filename'
64 * with the message given by strerror(errno).
66 #define LOG_SQLITE(db, level, cmd) do { GNUNET_log(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db)); } while(0)
69 #define SQLITE3_EXEC(db, cmd) do { emsg = NULL; if (SQLITE_OK != sqlite3_exec(db, cmd, NULL, NULL, &emsg)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, _("`%s' failed at %s:%d with error: %s\n"), "sqlite3_exec", __FILE__, __LINE__, emsg); sqlite3_free(emsg); } } while(0)
73 * @brief Prepare a SQL statement
76 sq_prepare (sqlite3 * dbh, const char *zSql, /* SQL statement, UTF-8 encoded */
77 sqlite3_stmt ** ppStmt)
78 { /* OUT: Statement handle */
81 return sqlite3_prepare (dbh,
82 zSql, strlen (zSql), ppStmt, (const char **) &dummy);
87 * Store an item in the datastore.
89 * @param cls closure (our "struct Plugin")
90 * @param key key to store data under
91 * @param size number of bytes in data
92 * @param data data to store
93 * @param type type of the value
94 * @param discard_time when to discard the value in any case
95 * @return 0 on error, number of bytes used otherwise
98 sqlite_plugin_put (void *cls,
99 const GNUNET_HashCode * key,
102 enum GNUNET_BLOCK_Type type,
103 struct GNUNET_TIME_Absolute discard_time)
105 struct Plugin *plugin = cls;
109 #if DEBUG_DATACACHE_SQLITE
110 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
111 "Processing `%s' of %u bytes with key `%4s' and expiration %llums\n",
116 GNUNET_TIME_absolute_get_remaining (discard_time).rel_value);
118 dval = (int64_t) discard_time.abs_value;
121 if (sq_prepare (plugin->dbh,
123 "(type, expire, key, value) "
124 "VALUES (?, ?, ?, ?)", &stmt) != SQLITE_OK)
126 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
127 _("`%s' failed at %s:%d with error: %s\n"),
128 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (plugin->dbh));
131 if ((SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) ||
132 (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, dval)) ||
133 (SQLITE_OK != sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode),
134 SQLITE_TRANSIENT)) ||
135 (SQLITE_OK != sqlite3_bind_blob (stmt, 4, data, size, SQLITE_TRANSIENT)))
137 LOG_SQLITE (plugin->dbh,
138 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
140 sqlite3_finalize (stmt);
143 if (SQLITE_DONE != sqlite3_step (stmt))
145 LOG_SQLITE (plugin->dbh,
146 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
148 sqlite3_finalize (stmt);
151 if (SQLITE_OK != sqlite3_finalize (stmt))
152 LOG_SQLITE (plugin->dbh,
153 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
155 return size + OVERHEAD;
160 * Iterate over the results for a particular key
163 * @param cls closure (our "struct Plugin")
165 * @param type entries of which type are relevant?
166 * @param iter maybe NULL (to just count)
167 * @param iter_cls closure for iter
168 * @return the number of results found
171 sqlite_plugin_get (void *cls,
172 const GNUNET_HashCode * key,
173 enum GNUNET_BLOCK_Type type,
174 GNUNET_DATACACHE_Iterator iter, void *iter_cls)
176 struct Plugin *plugin = cls;
178 struct GNUNET_TIME_Absolute now;
179 struct GNUNET_TIME_Absolute exp;
188 now = GNUNET_TIME_absolute_get ();
189 #if DEBUG_DATACACHE_SQLITE
190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
191 "Processing `%s' for key `%4s'\n", "GET", GNUNET_h2s (key));
193 if (sq_prepare (plugin->dbh,
194 "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?",
197 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
198 _("`%s' failed at %s:%d with error: %s\n"),
199 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (plugin->dbh));
202 ntime = (int64_t) now.abs_value;
203 GNUNET_assert (ntime >= 0);
205 sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
206 SQLITE_TRANSIENT)) ||
208 sqlite3_bind_int (stmt, 2, type)) ||
209 (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value)))
211 LOG_SQLITE (plugin->dbh,
212 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
214 sqlite3_finalize (stmt);
218 if (SQLITE_ROW != sqlite3_step (stmt))
220 LOG_SQLITE (plugin->dbh,
221 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
223 sqlite3_finalize (stmt);
226 total = sqlite3_column_int (stmt, 0);
227 sqlite3_finalize (stmt);
228 if ((total == 0) || (iter == NULL))
232 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
235 off = (off + 1) % total;
236 GNUNET_snprintf (scratch,
238 "SELECT value,expire FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
240 if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
242 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
243 _("`%s' failed at %s:%d with error: %s\n"),
244 "sq_prepare", __FILE__, __LINE__,
245 sqlite3_errmsg (plugin->dbh));
249 sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
250 SQLITE_TRANSIENT)) ||
252 sqlite3_bind_int (stmt, 2, type)) ||
253 (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value)))
255 LOG_SQLITE (plugin->dbh,
256 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
258 sqlite3_finalize (stmt);
261 if (sqlite3_step (stmt) != SQLITE_ROW)
263 size = sqlite3_column_bytes (stmt, 0);
264 dat = sqlite3_column_blob (stmt, 0);
265 exp.abs_value = sqlite3_column_int64 (stmt, 1);
266 ntime = (int64_t) exp.abs_value;
267 if (ntime == INT64_MAX)
268 exp = GNUNET_TIME_UNIT_FOREVER_ABS;
270 if (GNUNET_OK != iter (iter_cls, exp, key, size, dat, type))
272 sqlite3_finalize (stmt);
275 sqlite3_finalize (stmt);
282 * Delete the entry with the lowest expiration value
283 * from the datacache right now.
285 * @param cls closure (our "struct Plugin")
286 * @return GNUNET_OK on success, GNUNET_SYSERR on error
289 sqlite_plugin_del (void *cls)
291 struct Plugin *plugin = cls;
299 #if DEBUG_DATACACHE_SQLITE
300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing `%s'\n", "DEL");
304 if (sq_prepare (plugin->dbh,
305 "SELECT type, key, value FROM ds090 ORDER BY expire ASC LIMIT 1",
308 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
309 _("`%s' failed at %s:%d with error: %s\n"),
310 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (plugin->dbh));
312 (void) sqlite3_finalize (stmt);
313 return GNUNET_SYSERR;
315 if (SQLITE_ROW != sqlite3_step (stmt))
317 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
318 _("`%s' failed at %s:%d with error: %s\n"),
319 "sqlite3_step", __FILE__, __LINE__,
320 sqlite3_errmsg (plugin->dbh));
321 (void) sqlite3_finalize (stmt);
322 return GNUNET_SYSERR;
324 dtype = sqlite3_column_int (stmt, 0);
325 GNUNET_break (sqlite3_column_bytes (stmt, 1) == sizeof (GNUNET_HashCode));
326 dsize = sqlite3_column_bytes (stmt, 2);
327 GNUNET_assert (dsize <= sizeof (blob));
328 memcpy (blob, sqlite3_column_blob (stmt, 2), dsize);
329 memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (GNUNET_HashCode));
330 if (SQLITE_OK != sqlite3_finalize (stmt))
331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
332 _("`%s' failed at %s:%d with error: %s\n"),
333 "sqlite3_step", __FILE__, __LINE__,
334 sqlite3_errmsg (plugin->dbh));
335 if (sq_prepare (plugin->dbh,
337 "WHERE key=? AND value=? AND type=?", &dstmt) != SQLITE_OK)
339 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
340 _("`%s' failed at %s:%d with error: %s\n"),
341 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (plugin->dbh));
343 (void) sqlite3_finalize (stmt);
344 return GNUNET_SYSERR;
347 sqlite3_bind_blob (dstmt,
349 sizeof (GNUNET_HashCode),
350 SQLITE_TRANSIENT)) ||
352 sqlite3_bind_blob (dstmt,
355 SQLITE_TRANSIENT)) ||
356 (SQLITE_OK != sqlite3_bind_int (dstmt, 3, dtype)))
358 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
359 _("`%s' failed at %s:%d with error: %s\n"),
360 "sqlite3_bind", __FILE__, __LINE__,
361 sqlite3_errmsg (plugin->dbh));
362 (void) sqlite3_finalize (dstmt);
363 return GNUNET_SYSERR;
365 if (sqlite3_step (dstmt) != SQLITE_DONE)
367 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
368 _("`%s' failed at %s:%d with error: %s\n"),
369 "sqlite3_step", __FILE__, __LINE__,
370 sqlite3_errmsg (plugin->dbh));
371 (void) sqlite3_finalize (dstmt);
372 return GNUNET_SYSERR;
374 plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD);
375 if (SQLITE_OK != sqlite3_finalize (dstmt))
376 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
377 _("`%s' failed at %s:%d with error: %s\n"),
378 "sqlite3_finalize", __FILE__, __LINE__,
379 sqlite3_errmsg (plugin->dbh));
385 * Entry point for the plugin.
387 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
388 * @return the plugin's closure (our "struct Plugin")
391 libgnunet_plugin_datacache_sqlite_init (void *cls)
393 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
394 struct GNUNET_DATACACHE_PluginFunctions *api;
395 struct Plugin *plugin;
401 fn = GNUNET_DISK_mktemp ("gnunet-datacache");
408 fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn), nl_langinfo (CODESET));
411 fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn), "UTF-8");
413 if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
416 GNUNET_free (fn_utf8);
421 SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
422 SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
423 SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
424 SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
425 SQLITE3_EXEC (dbh, "PRAGMA count_changes=OFF");
426 SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
428 "CREATE TABLE ds090 ("
429 " type INTEGER NOT NULL DEFAULT 0,"
430 " expire INTEGER NOT NULL DEFAULT 0,"
431 " key BLOB NOT NULL DEFAULT '',"
432 " value BLOB NOT NULL DEFAULT '')");
433 SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)");
434 plugin = GNUNET_malloc (sizeof (struct Plugin));
437 plugin->fn = fn_utf8;
438 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
440 api->get = &sqlite_plugin_get;
441 api->put = &sqlite_plugin_put;
442 api->del = &sqlite_plugin_del;
443 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
444 "sqlite", _("Sqlite datacache running\n"));
448 // explain SELECT type FROM gn090 WHERE NOT EXISTS (SELECT 1 from gn090 WHERE expire < 42 LIMIT 1) OR expire < 42 ORDER BY repl DESC, Random() LIMIT 1;
452 * Exit point from the plugin.
454 * @param cls closure (our "struct Plugin")
458 libgnunet_plugin_datacache_sqlite_done (void *cls)
460 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
461 struct Plugin *plugin = api->cls;
464 #if SQLITE_VERSION_NUMBER >= 3007000
468 #if !WINDOWS || defined(__CYGWIN__)
469 if (0 != UNLINK (plugin->fn))
470 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
471 GNUNET_free (plugin->fn);
473 result = sqlite3_close (plugin->dbh);
474 #if SQLITE_VERSION_NUMBER >= 3007000
475 if (result == SQLITE_BUSY)
477 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
480 ("Tried to close sqlite without finalizing all prepared statements.\n"));
481 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
485 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
486 "sqlite", "Closing statement %p\n", stmt);
488 result = sqlite3_finalize (stmt);
490 if (result != SQLITE_OK)
491 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
493 "Failed to close statement %p: %d\n", stmt, result);
495 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
497 result = sqlite3_close (plugin->dbh);
500 if (SQLITE_OK != result)
501 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
503 #if WINDOWS && !defined(__CYGWIN__)
504 if (0 != UNLINK (plugin->fn))
505 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
506 GNUNET_free (plugin->fn);
508 GNUNET_free (plugin);
515 /* end of plugin_datacache_sqlite.c */