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 2, 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 "plugin_datacache.h"
31 #define DEBUG_DATACACHE_SQLITE GNUNET_YES
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 */
80 return sqlite3_prepare (dbh,
82 strlen (zSql), ppStmt, (const char **) &dummy);
87 * Store an item in the datastore.
89 * @param key key to store data under
90 * @param size number of bytes in data
91 * @param data data to store
92 * @param type type of the value
93 * @param discard_time when to discard the value in any case
94 * @return 0 on error, number of bytes used otherwise
97 sqlite_plugin_put (void *cls,
98 const GNUNET_HashCode * key,
102 struct GNUNET_TIME_Absolute discard_time)
104 struct Plugin *plugin = cls;
107 #if DEBUG_DATACACHE_SQLITE
108 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
109 "Processing `%s' of %u bytes with key `%4s' and expiration %llums\n",
113 (unsigned long long) GNUNET_TIME_absolute_get_remaining (discard_time).value);
115 if (sq_prepare (plugin->dbh,
117 "(type, expire, key, value) "
118 "VALUES (?, ?, ?, ?)", &stmt) != SQLITE_OK)
120 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
121 _("`%s' failed at %s:%d with error: %s\n"),
122 "sq_prepare", __FILE__, __LINE__,
123 sqlite3_errmsg (plugin->dbh));
126 if ( (SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) ||
127 (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, discard_time.value)) ||
128 (SQLITE_OK != sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode),
129 SQLITE_TRANSIENT)) ||
130 (SQLITE_OK != sqlite3_bind_blob (stmt, 4, data, size, SQLITE_TRANSIENT)))
132 LOG_SQLITE (plugin->dbh,
133 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
135 sqlite3_finalize (stmt);
138 if (SQLITE_DONE != sqlite3_step (stmt))
140 LOG_SQLITE (plugin->dbh,
141 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
143 sqlite3_finalize (stmt);
146 if (SQLITE_OK != sqlite3_finalize (stmt))
147 LOG_SQLITE (plugin->dbh,
148 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
150 return size + OVERHEAD;
155 * Iterate over the results for a particular key
159 * @param type entries of which type are relevant?
160 * @param iter maybe NULL (to just count)
161 * @return the number of results found
164 sqlite_plugin_get (void *cls,
165 const GNUNET_HashCode * key,
167 GNUNET_DATACACHE_Iterator iter,
170 struct Plugin *plugin = cls;
172 struct GNUNET_TIME_Absolute now;
180 now = GNUNET_TIME_absolute_get ();
182 #if DEBUG_DATACACHE_SQLITE
183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
184 "Processing `%s' for key `%4s'\n",
188 if (sq_prepare (plugin->dbh,
189 "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?",
192 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
193 _("`%s' failed at %s:%d with error: %s\n"),
194 "sq_prepare", __FILE__, __LINE__,
195 sqlite3_errmsg (plugin->dbh));
198 sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
200 sqlite3_bind_int (stmt, 2, type);
201 sqlite3_bind_int64 (stmt, 3, now.value);
202 if (SQLITE_ROW != sqlite3_step (stmt))
204 LOG_SQLITE (plugin->dbh,
205 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
207 sqlite3_finalize (stmt);
210 total = sqlite3_column_int (stmt, 0);
211 sqlite3_finalize (stmt);
212 if ( (total == 0) || (iter == NULL) )
216 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
219 off = (off + 1) % total;
220 GNUNET_snprintf (scratch,
222 "SELECT value FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u",
224 if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK)
226 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
227 _("`%s' failed at %s:%d with error: %s\n"),
228 "sq_prepare", __FILE__, __LINE__,
229 sqlite3_errmsg (plugin->dbh));
232 sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode),
234 sqlite3_bind_int (stmt, 2, type);
235 sqlite3_bind_int64 (stmt, 3, now.value);
236 if (sqlite3_step (stmt) != SQLITE_ROW)
238 size = sqlite3_column_bytes (stmt, 0);
239 dat = sqlite3_column_blob (stmt, 1);
241 if (GNUNET_OK != iter (iter_cls,
247 sqlite3_finalize (stmt);
250 sqlite3_finalize (stmt);
257 * Delete the entry with the lowest expiration value
258 * from the datacache right now.
260 * @return GNUNET_OK on success, GNUNET_SYSERR on error
263 sqlite_plugin_del (void *cls)
265 struct Plugin *plugin = cls;
271 #if DEBUG_DATACACHE_SQLITE
272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
278 if ((sq_prepare (plugin->dbh,
279 "SELECT type, key, value FROM ds090 ORDER BY expire ASC LIMIT 1",
280 &stmt) != SQLITE_OK) ||
281 (sq_prepare (plugin->dbh,
283 "WHERE key=? AND value=? AND type=?",
284 &dstmt) != SQLITE_OK))
286 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
287 _("`%s' failed at %s:%d with error: %s\n"),
288 "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (plugin->dbh));
290 sqlite3_finalize (dstmt);
292 sqlite3_finalize (stmt);
293 return GNUNET_SYSERR;
295 if (SQLITE_ROW != sqlite3_step (stmt))
297 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
298 _("`%s' failed at %s:%d with error: %s\n"),
299 "sqlite3_step", __FILE__, __LINE__,
300 sqlite3_errmsg (plugin->dbh));
301 sqlite3_finalize (dstmt);
302 sqlite3_finalize (stmt);
303 return GNUNET_SYSERR;
305 dtype = sqlite3_column_int (stmt, 0);
306 GNUNET_break (sqlite3_column_bytes (stmt, 1) == sizeof (GNUNET_HashCode));
307 dsize = sqlite3_column_bytes (stmt, 2);
308 sqlite3_bind_blob (dstmt,
309 1, sqlite3_column_blob (stmt, 1),
310 sizeof (GNUNET_HashCode),
312 sqlite3_bind_blob (dstmt,
313 2, sqlite3_column_blob (stmt, 2),
316 sqlite3_bind_int (dstmt, 3, dtype);
317 if (sqlite3_step (dstmt) != SQLITE_DONE)
319 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
320 _("`%s' failed at %s:%d with error: %s\n"),
321 "sqlite3_step", __FILE__, __LINE__,
322 sqlite3_errmsg (plugin->dbh));
323 sqlite3_finalize (dstmt);
324 sqlite3_finalize (stmt);
325 return GNUNET_SYSERR;
327 plugin->env->delete_notify (plugin->env->cls,
328 sqlite3_column_blob (stmt, 1),
330 sqlite3_finalize (dstmt);
331 sqlite3_finalize (stmt);
337 * Entry point for the plugin.
340 libgnunet_plugin_datacache_sqlite_init (void *cls)
342 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
343 struct GNUNET_DATACACHE_PluginFunctions *api;
344 struct Plugin *plugin;
353 tmpdir = getenv ("TMPDIR");
354 tmpdir = tmpdir ? tmpdir : "/tmp";
356 #define TEMPLATE "/gnunet-dstoreXXXXXX"
357 tmpl = GNUNET_malloc (strlen (tmpdir) + sizeof (TEMPLATE) + 1);
358 strcpy (tmpl, tmpdir);
359 strcat (tmpl, TEMPLATE);
362 fn = (char *) GNUNET_malloc (MAX_PATH + 1);
363 plibc_conv_to_win_path (tmpl, fn);
376 fn_utf8 = GNUNET_STRINGS_to_utf8 (fn, strlen (fn),
378 nl_langinfo (CODESET)
380 "UTF-8" /* good luck */
383 if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
386 GNUNET_free (fn_utf8);
391 SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
392 SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
393 SQLITE3_EXEC (dbh, "PRAGMA count_changes=OFF");
394 SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
396 "CREATE TABLE ds090 ("
397 " type INTEGER NOT NULL DEFAULT 0,"
398 " expire INTEGER NOT NULL DEFAULT 0,"
399 " key BLOB NOT NULL DEFAULT '',"
400 " value BLOB NOT NULL DEFAULT '')");
401 SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds080 (key,type,expire)");
402 plugin = GNUNET_malloc (sizeof (struct Plugin));
405 plugin->fn = fn_utf8;
406 api = GNUNET_malloc (sizeof (struct GNUNET_DATACACHE_PluginFunctions));
408 api->get = &sqlite_plugin_get;
409 api->put = &sqlite_plugin_put;
410 api->del = &sqlite_plugin_del;
411 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
412 "sqlite", _("Sqlite datacache running\n"));
418 * Exit point from the plugin.
421 libgnunet_plugin_datacache_sqlite_done (void *cls)
423 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
424 struct Plugin *plugin = api->cls;
427 GNUNET_free (plugin->fn);
428 sqlite3_close (plugin->dbh);
429 GNUNET_free (plugin);
436 /* end of plugin_datacache_sqlite.c */