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_EXTRA_LOGGING
33 #define LOG(kind,...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__)
35 #define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-sqlite", op, fn)
39 * How much overhead do we assume per entry in the
42 #define OVERHEAD (sizeof(GNUNET_HashCode) + 32)
45 * Context for all functions in this plugin.
50 * Our execution environment.
52 struct GNUNET_DATACACHE_PluginEnvironment *env;
55 * Handle to the sqlite database.
60 * Filename used for the DB.
67 * Log an error message at log-level 'level' that indicates
68 * a failure of the command 'cmd' on file 'filename'
69 * with the message given by strerror(errno).
71 #define LOG_SQLITE(db, level, cmd) do { LOG (level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db)); } while(0)
74 #define SQLITE3_EXEC(db, cmd) do { emsg = NULL; if (SQLITE_OK != sqlite3_exec(db, cmd, NULL, NULL, &emsg)) { 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)
78 * @brief Prepare a SQL statement
81 sq_prepare (sqlite3 * dbh, const char *zSql, /* SQL statement, UTF-8 encoded */
82 sqlite3_stmt ** ppStmt)
83 { /* OUT: Statement handle */
86 return sqlite3_prepare (dbh, zSql, strlen (zSql), ppStmt,
87 (const char **) &dummy);
92 * Store an item in the datastore.
94 * @param cls closure (our "struct Plugin")
95 * @param key key to store data under
96 * @param size number of bytes in data
97 * @param data data to store
98 * @param type type of the value
99 * @param discard_time when to discard the value in any case
100 * @return 0 on error, number of bytes used otherwise
103 sqlite_plugin_put (void *cls, const GNUNET_HashCode * key, size_t size,
104 const char *data, enum GNUNET_BLOCK_Type type,
105 struct GNUNET_TIME_Absolute discard_time)
107 struct Plugin *plugin = cls;
111 #if DEBUG_DATACACHE_SQLITE
112 LOG (GNUNET_ERROR_TYPE_DEBUG,
113 "Processing `%s' of %u bytes with key `%4s' and expiration %llums\n",
114 "PUT", (unsigned int) size, GNUNET_h2s (key),
116 GNUNET_TIME_absolute_get_remaining (discard_time).rel_value);
118 dval = (int64_t) discard_time.abs_value;
123 "INSERT INTO ds090 (type, expire, key, value) VALUES (?, ?, ?, ?)",
126 LOG_SQLITE (plugin->dbh,
127 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
131 if ((SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) ||
132 (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, dval)) ||
134 sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode),
135 SQLITE_TRANSIENT)) ||
136 (SQLITE_OK != sqlite3_bind_blob (stmt, 4, data, size, SQLITE_TRANSIENT)))
138 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
140 sqlite3_finalize (stmt);
143 if (SQLITE_DONE != sqlite3_step (stmt))
145 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
147 sqlite3_finalize (stmt);
150 if (SQLITE_OK != sqlite3_finalize (stmt))
151 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
153 return size + OVERHEAD;
158 * Iterate over the results for a particular key
161 * @param cls closure (our "struct Plugin")
163 * @param type entries of which type are relevant?
164 * @param iter maybe NULL (to just count)
165 * @param iter_cls closure for iter
166 * @return the number of results found
169 sqlite_plugin_get (void *cls, const GNUNET_HashCode * key,
170 enum GNUNET_BLOCK_Type type, GNUNET_DATACACHE_Iterator iter,
173 struct Plugin *plugin = cls;
175 struct GNUNET_TIME_Absolute now;
176 struct GNUNET_TIME_Absolute exp;
185 now = GNUNET_TIME_absolute_get ();
186 #if DEBUG_DATACACHE_SQLITE
187 LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing `%s' for key `%4s'\n", "GET",
192 "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?",
195 LOG_SQLITE (plugin->dbh,
196 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
200 ntime = (int64_t) now.abs_value;
201 GNUNET_assert (ntime >= 0);
203 sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
204 SQLITE_TRANSIENT)) ||
205 (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) ||
206 (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value)))
208 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
210 sqlite3_finalize (stmt);
214 if (SQLITE_ROW != sqlite3_step (stmt))
216 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
218 sqlite3_finalize (stmt);
219 #if DEBUG_DATACACHE_SQLITE
220 LOG (GNUNET_ERROR_TYPE_DEBUG,
221 "No content found when processing `%s' for key `%4s'\n",
222 "GET", GNUNET_h2s (key));
226 total = sqlite3_column_int (stmt, 0);
227 sqlite3_finalize (stmt);
228 if ((total == 0) || (iter == NULL))
230 #if DEBUG_DATACACHE_SQLITE
232 LOG (GNUNET_ERROR_TYPE_DEBUG,
233 "No content found when processing `%s' for key `%4s'\n",
234 "GET", GNUNET_h2s (key));
240 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
243 off = (off + 1) % total;
244 GNUNET_snprintf (scratch, sizeof (scratch),
245 "SELECT value,expire FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
247 if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
249 LOG_SQLITE (plugin->dbh,
250 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
255 sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
256 SQLITE_TRANSIENT)) ||
257 (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) ||
258 (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value)))
260 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
262 sqlite3_finalize (stmt);
265 if (sqlite3_step (stmt) != SQLITE_ROW)
267 size = sqlite3_column_bytes (stmt, 0);
268 dat = sqlite3_column_blob (stmt, 0);
269 exp.abs_value = sqlite3_column_int64 (stmt, 1);
270 ntime = (int64_t) exp.abs_value;
271 if (ntime == INT64_MAX)
272 exp = GNUNET_TIME_UNIT_FOREVER_ABS;
274 #if DEBUG_DATACACHE_SQLITE
275 LOG (GNUNET_ERROR_TYPE_DEBUG,
276 "Found %u-byte result when processing `%s' for key `%4s'\n",
281 if (GNUNET_OK != iter (iter_cls, exp, key, size, dat, type))
283 sqlite3_finalize (stmt);
286 sqlite3_finalize (stmt);
293 * Delete the entry with the lowest expiration value
294 * from the datacache right now.
296 * @param cls closure (our "struct Plugin")
297 * @return GNUNET_OK on success, GNUNET_SYSERR on error
300 sqlite_plugin_del (void *cls)
302 struct Plugin *plugin = cls;
303 unsigned long long rowid;
309 #if DEBUG_DATACACHE_SQLITE
310 LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing `%s'\n", "DEL");
316 "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC LIMIT 1",
319 LOG_SQLITE (plugin->dbh,
320 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
323 (void) sqlite3_finalize (stmt);
324 return GNUNET_SYSERR;
326 if (SQLITE_ROW != sqlite3_step (stmt))
328 LOG_SQLITE (plugin->dbh,
329 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
331 (void) sqlite3_finalize (stmt);
332 return GNUNET_SYSERR;
334 rowid = sqlite3_column_int64 (stmt, 0);
335 GNUNET_assert (sqlite3_column_bytes (stmt, 1) == sizeof (GNUNET_HashCode));
336 memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (GNUNET_HashCode));
337 dsize = sqlite3_column_bytes (stmt, 2);
338 if (SQLITE_OK != sqlite3_finalize (stmt))
339 LOG_SQLITE (plugin->dbh,
340 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
343 (plugin->dbh, "DELETE FROM ds090 WHERE _ROWID_=?",
344 &dstmt) != SQLITE_OK)
346 LOG_SQLITE (plugin->dbh,
347 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
350 (void) sqlite3_finalize (stmt);
351 return GNUNET_SYSERR;
354 sqlite3_bind_int64 (dstmt, 1, rowid))
356 LOG_SQLITE (plugin->dbh,
357 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
359 (void) sqlite3_finalize (dstmt);
360 return GNUNET_SYSERR;
362 if (sqlite3_step (dstmt) != SQLITE_DONE)
364 LOG_SQLITE (plugin->dbh,
365 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
367 (void) sqlite3_finalize (dstmt);
368 return GNUNET_SYSERR;
370 plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD);
371 if (SQLITE_OK != sqlite3_finalize (dstmt))
372 LOG_SQLITE (plugin->dbh,
373 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
380 * Entry point for the plugin.
382 * @param cls closure (the "struct GNUNET_DATACACHE_PluginEnvironmnet")
383 * @return the plugin's closure (our "struct Plugin")
386 libgnunet_plugin_datacache_sqlite_init (void *cls)
388 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
389 struct GNUNET_DATACACHE_PluginFunctions *api;
390 struct Plugin *plugin;
396 fn = GNUNET_DISK_mktemp ("gnunet-datacache");
403 fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn), nl_langinfo (CODESET));
406 fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn), "UTF-8");
408 if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
411 GNUNET_free (fn_utf8);
416 SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
417 SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
418 SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
419 SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
420 SQLITE3_EXEC (dbh, "PRAGMA count_changes=OFF");
421 SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
423 "CREATE TABLE ds090 (" " type INTEGER NOT NULL DEFAULT 0,"
424 " expire INTEGER NOT NULL DEFAULT 0,"
425 " key BLOB NOT NULL DEFAULT '',"
426 " value BLOB NOT NULL DEFAULT '')");
427 SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)");
428 SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire ON ds090 (expire)");
429 plugin = GNUNET_malloc (sizeof (struct Plugin));
432 plugin->fn = fn_utf8;
433 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
435 api->get = &sqlite_plugin_get;
436 api->put = &sqlite_plugin_put;
437 api->del = &sqlite_plugin_del;
438 LOG (GNUNET_ERROR_TYPE_INFO,
439 _("Sqlite datacache running\n"));
445 * Exit point from the plugin.
447 * @param cls closure (our "struct Plugin")
451 libgnunet_plugin_datacache_sqlite_done (void *cls)
453 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
454 struct Plugin *plugin = api->cls;
457 #if SQLITE_VERSION_NUMBER >= 3007000
461 #if !WINDOWS || defined(__CYGWIN__)
462 if (0 != UNLINK (plugin->fn))
463 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
464 GNUNET_free (plugin->fn);
466 result = sqlite3_close (plugin->dbh);
467 #if SQLITE_VERSION_NUMBER >= 3007000
468 if (result == SQLITE_BUSY)
470 LOG (GNUNET_ERROR_TYPE_WARNING,
471 _("Tried to close sqlite without finalizing all prepared statements.\n"));
472 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
476 LOG (GNUNET_ERROR_TYPE_DEBUG,
477 "Closing statement %p\n", stmt);
479 result = sqlite3_finalize (stmt);
481 if (result != SQLITE_OK)
482 LOG (GNUNET_ERROR_TYPE_DEBUG,
483 "Failed to close statement %p: %d\n", stmt, result);
485 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
487 result = sqlite3_close (plugin->dbh);
490 if (SQLITE_OK != result)
491 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
493 #if WINDOWS && !defined(__CYGWIN__)
494 if (0 != UNLINK (plugin->fn))
495 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
496 GNUNET_free (plugin->fn);
498 GNUNET_free (plugin);
505 /* end of plugin_datacache_sqlite.c */