X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fdatacache%2Fplugin_datacache_sqlite.c;h=5567077d3fd8738ca4ff39f20e477d62b885d481;hb=2105059516320800eaa8fff1196b58f29a50ba7c;hp=e386b55de7491861d420d24dbbfe5323c98a0a4b;hpb=c890b76cde9288a8d9f2faa3046fbb06341c8082;p=oweals%2Fgnunet.git diff --git a/src/datacache/plugin_datacache_sqlite.c b/src/datacache/plugin_datacache_sqlite.c index e386b55de..5567077d3 100644 --- a/src/datacache/plugin_datacache_sqlite.c +++ b/src/datacache/plugin_datacache_sqlite.c @@ -1,10 +1,10 @@ /* - This file is part of GNUnet. - (C) 2006, 2007, 2008 Christian Grothoff (and other contributing authors) + This file is part of GNUnet + Copyright (C) 2006, 2009, 2015 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but @@ -14,663 +14,771 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** - * @file applications/dstore_sqlite/dstore.c - * @brief SQLite based implementation of the dstore service + * @file datacache/plugin_datacache_sqlite.c + * @brief sqlite for an implementation of a database backend for the datacache * @author Christian Grothoff - * @todo Indexes, statistics - * - * Database: SQLite */ - #include "platform.h" -#include "gnunet_util.h" -#include "gnunet_dstore_service.h" -#include "gnunet_stats_service.h" +#include "gnunet_util_lib.h" +#include "gnunet_datacache_plugin.h" #include -#define DEBUG_DSTORE GNUNET_NO +#define LOG(kind,...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__) -/** - * Maximum size for an individual item. - */ -#define MAX_CONTENT_SIZE 65536 +#define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache-sqlite", op, fn) -/** - * Bytes used - */ -static unsigned long long payload; /** - * Maximum bytes available + * How much overhead do we assume per entry in the + * datacache? */ -static unsigned long long quota; +#define OVERHEAD (sizeof(struct GNUNET_HashCode) + 32) /** - * Filename of this database + * Context for all functions in this plugin. */ -static char *fn; -static char *fn_utf8; +struct Plugin +{ + /** + * Our execution environment. + */ + struct GNUNET_DATACACHE_PluginEnvironment *env; + + /** + * Handle to the sqlite database. + */ + sqlite3 *dbh; + + /** + * Filename used for the DB. + */ + char *fn; -static GNUNET_CoreAPIForPlugins *coreAPI; + /** + * Number of key-value pairs in the database. + */ + unsigned int num_items; +}; -static struct GNUNET_Mutex *lock; /** - * Statistics service. + * Log an error message at log-level @a level that indicates + * a failure of the command @a cmd with the error from the database @a db + * + * @param db database handle + * @param level log level + * @param cmd failed command */ -static GNUNET_Stats_ServiceAPI *stats; +#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) -static unsigned int stat_dstore_size; - -static unsigned int stat_dstore_quota; /** - * Estimate of the per-entry overhead (including indices). + * Execute SQL statement. + * + * @param db database handle + * @param cmd SQL command to execute */ -#define OVERHEAD ((4*2+4*2+8*2+8*2+sizeof(GNUNET_HashCode)*5+32)) - -struct GNUNET_BloomFilter *bloom; +#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) -static char *bloom_name; /** * @brief Prepare a SQL statement + * + * @param dbh database handle + * @param zsql SQL statement text + * @param[out] ppStmt set to the prepared statement + * @return 0 on success */ static int -sq_prepare (sqlite3 * dbh, const char *zSql, /* SQL statement, UTF-8 encoded */ - sqlite3_stmt ** ppStmt) +sq_prepare (sqlite3 *dbh, + const char *zSql, /* SQL statement, UTF-8 encoded */ + sqlite3_stmt **ppStmt) { /* OUT: Statement handle */ char *dummy; + return sqlite3_prepare (dbh, - zSql, - strlen (zSql), ppStmt, (const char **) &dummy); + zSql, strlen (zSql), + ppStmt, + (const char **) &dummy); } -#define SQLITE3_EXEC(db, cmd) do { emsg = NULL; if (SQLITE_OK != sqlite3_exec(db, cmd, NULL, NULL, &emsg)) { GNUNET_GE_LOG(coreAPI->ectx, GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, _("`%s' failed at %s:%d with error: %s\n"), "sqlite3_exec", __FILE__, __LINE__, emsg); sqlite3_free(emsg); } } while(0) /** - * Log an error message at log-level 'level' that indicates - * a failure of the command 'cmd' on file 'filename' - * with the message given by strerror(errno). + * Store an item in the datastore. + * + * @param cls closure (our `struct Plugin`) + * @param key key to store @a data under + * @param size number of bytes in @a data + * @param data data to store + * @param type type of the value + * @param discard_time when to discard the value in any case + * @param path_info_len number of entries in @a path_info + * @param path_info array of peers that have processed the request + * @return 0 if duplicate, -1 on error, number of bytes used otherwise */ -#define LOG_SQLITE(db, level, cmd) do { GNUNET_GE_LOG(coreAPI->ectx, level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db)); } while(0) - -static void -db_init (sqlite3 * dbh) +static ssize_t +sqlite_plugin_put (void *cls, + const struct GNUNET_HashCode *key, + size_t size, + const char *data, + enum GNUNET_BLOCK_Type type, + struct GNUNET_TIME_Absolute discard_time, + unsigned int path_info_len, + const struct GNUNET_PeerIdentity *path_info) { - char *emsg; - - SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY"); - SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF"); - SQLITE3_EXEC (dbh, "PRAGMA count_changes=OFF"); - SQLITE3_EXEC (dbh, "PRAGMA page_size=4092"); - SQLITE3_EXEC (dbh, - "CREATE TABLE ds080 (" - " size INTEGER NOT NULL DEFAULT 0," - " type INTEGER NOT NULL DEFAULT 0," - " puttime INTEGER NOT NULL DEFAULT 0," - " expire INTEGER NOT NULL DEFAULT 0," - " key BLOB NOT NULL DEFAULT ''," - " vhash BLOB NOT NULL DEFAULT ''," - " value BLOB NOT NULL DEFAULT '')"); - SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds080 (key,type,expire)"); - SQLITE3_EXEC (dbh, - "CREATE INDEX idx_allidx ON ds080 (key,vhash,type,size)"); - SQLITE3_EXEC (dbh, "CREATE INDEX idx_puttime ON ds080 (puttime)"); + struct Plugin *plugin = cls; + sqlite3_stmt *stmt; + int64_t dval; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing PUT of %u bytes with key `%4s' and expiration %s\n", + (unsigned int) size, + GNUNET_h2s (key), + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (discard_time), GNUNET_YES)); + dval = (int64_t) discard_time.abs_value_us; + if (dval < 0) + dval = INT64_MAX; + if (sq_prepare + (plugin->dbh, + "INSERT INTO ds090 (type, expire, key, value, path) VALUES (?, ?, ?, ?, ?)", + &stmt) != SQLITE_OK) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sq_prepare"); + return -1; + } + if ((SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) || + (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, dval)) || + (SQLITE_OK != + sqlite3_bind_blob (stmt, 3, + key, sizeof (struct GNUNET_HashCode), + SQLITE_TRANSIENT)) || + (SQLITE_OK != sqlite3_bind_blob (stmt, 4, + data, size, + SQLITE_TRANSIENT)) || + (SQLITE_OK != sqlite3_bind_blob (stmt, 5, + path_info, + path_info_len * sizeof (struct GNUNET_PeerIdentity), + SQLITE_TRANSIENT))) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_xxx"); + sqlite3_finalize (stmt); + return -1; + } + if (SQLITE_DONE != sqlite3_step (stmt)) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + sqlite3_finalize (stmt); + return -1; + } + plugin->num_items++; + if (SQLITE_OK != sqlite3_finalize (stmt)) + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_finalize"); + return size + OVERHEAD; } -static int -db_reset () + +/** + * Iterate over the results for a particular key + * in the datastore. + * + * @param cls closure (our `struct Plugin`) + * @param key + * @param type entries of which type are relevant? + * @param iter maybe NULL (to just count) + * @param iter_cls closure for @a iter + * @return the number of results found + */ +static unsigned int +sqlite_plugin_get (void *cls, + const struct GNUNET_HashCode *key, + enum GNUNET_BLOCK_Type type, + GNUNET_DATACACHE_Iterator iter, + void *iter_cls) { - int fd; - sqlite3 *dbh; - char *tmpl; - const char *tmpdir; + struct Plugin *plugin = cls; + sqlite3_stmt *stmt; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute exp; + unsigned int size; + const char *dat; + unsigned int cnt; + unsigned int off; + unsigned int total; + unsigned int psize; + char scratch[256]; + int64_t ntime; + const struct GNUNET_PeerIdentity *path; + + now = GNUNET_TIME_absolute_get (); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing GET for key `%4s'\n", + GNUNET_h2s (key)); + if (sq_prepare + (plugin->dbh, + "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?", + &stmt) != SQLITE_OK) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sq_prepare"); + return 0; + } + ntime = (int64_t) now.abs_value_us; + GNUNET_assert (ntime >= 0); + if ((SQLITE_OK != + sqlite3_bind_blob (stmt, 1, key, sizeof (struct GNUNET_HashCode), + SQLITE_TRANSIENT)) || + (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) || + (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value_us))) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_xxx"); + sqlite3_finalize (stmt); + return 0; + } - if (fn != NULL) + if (SQLITE_ROW != sqlite3_step (stmt)) + { + LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite_step"); + sqlite3_finalize (stmt); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "No content found when processing GET for key `%4s'\n", + GNUNET_h2s (key)); + return 0; + } + total = sqlite3_column_int (stmt, 0); + sqlite3_finalize (stmt); + if ((0 == total) || (NULL == iter)) + { + if (0 == total) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "No content found when processing GET for key `%4s'\n", + GNUNET_h2s (key)); + return total; + } + + cnt = 0; + off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total); + while (cnt < total) + { + off = (off + 1) % total; + GNUNET_snprintf (scratch, sizeof (scratch), + "SELECT value,expire,path FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u", + off); + if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK) { - UNLINK (fn); - GNUNET_free (fn); - GNUNET_free (fn_utf8); + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sq_prepare"); + return cnt; } - payload = 0; - - tmpdir = getenv ("TMPDIR"); - tmpdir = tmpdir ? tmpdir : "/tmp"; - -#define TEMPLATE "/gnunet-dstoreXXXXXX" - tmpl = GNUNET_malloc (strlen (tmpdir) + sizeof (TEMPLATE) + 1); - strcpy (tmpl, tmpdir); - strcat (tmpl, TEMPLATE); -#undef TEMPLATE - -#ifdef MINGW - fn = (char *) GNUNET_malloc (MAX_PATH + 1); - plibc_conv_to_win_path (tmpl, fn); - GNUNET_free (tmpl); -#else - fn = tmpl; -#endif - fd = mkstemp (fn); - if (fd == -1) + if ((SQLITE_OK != + sqlite3_bind_blob (stmt, 1, + key, + sizeof (struct GNUNET_HashCode), + SQLITE_TRANSIENT)) || + (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) || + (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value_us))) { - GNUNET_GE_BREAK (NULL, 0); - GNUNET_free (fn); - fn = NULL; - return GNUNET_SYSERR; + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_xxx"); + sqlite3_finalize (stmt); + return cnt; } - CLOSE (fd); - fn_utf8 = GNUNET_convert_string_to_utf8 (coreAPI->ectx, fn, strlen (fn), -#ifdef ENABLE_NLS - nl_langinfo (CODESET) -#else - "UTF-8" /* good luck */ -#endif - ); - if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh)) + if (sqlite3_step (stmt) != SQLITE_ROW) + break; + size = sqlite3_column_bytes (stmt, 0); + dat = sqlite3_column_blob (stmt, 0); + exp.abs_value_us = sqlite3_column_int64 (stmt, 1); + psize = sqlite3_column_bytes (stmt, 2); + if (0 != psize % sizeof (struct GNUNET_PeerIdentity)) { - GNUNET_free (fn); - GNUNET_free (fn_utf8); - fn = NULL; - return GNUNET_SYSERR; + GNUNET_break (0); + psize = 0; } - db_init (dbh); - sqlite3_close (dbh); - return GNUNET_OK; + psize /= sizeof (struct GNUNET_PeerIdentity); + if (0 != psize) + path = sqlite3_column_blob (stmt, 2); + else + path = NULL; + ntime = (int64_t) exp.abs_value_us; + if (ntime == INT64_MAX) + exp = GNUNET_TIME_UNIT_FOREVER_ABS; + cnt++; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Found %u-byte result when processing GET for key `%4s'\n", + (unsigned int) size, + GNUNET_h2s (key)); + if (GNUNET_OK != iter (iter_cls, + key, + size, + dat, + type, + exp, + psize, + path)) + { + sqlite3_finalize (stmt); + break; + } + sqlite3_finalize (stmt); + } + return cnt; } + /** - * Check that we are within quota. - * @return GNUNET_OK if we are. + * Delete the entry with the lowest expiration value + * from the datacache right now. + * + * @param cls closure (our `struct Plugin`) + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ static int -checkQuota (sqlite3 * dbh) +sqlite_plugin_del (void *cls) { - GNUNET_HashCode dkey; - GNUNET_HashCode vhash; + struct Plugin *plugin = cls; + unsigned long long rowid; unsigned int dsize; - unsigned int dtype; sqlite3_stmt *stmt; sqlite3_stmt *dstmt; - int err; - - if (payload * 10 <= quota * 9) - return GNUNET_OK; /* we seem to be about 10% off */ -#if DEBUG_DSTORE - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER, - "DStore above qutoa (have %llu, allowed %llu), will delete some data.\n", - payload, quota); -#endif + struct GNUNET_HashCode hc; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing DEL\n"); stmt = NULL; dstmt = NULL; - if ((sq_prepare (dbh, - "SELECT size, type, key, vhash FROM ds080 ORDER BY puttime ASC LIMIT 1", - &stmt) != SQLITE_OK) || - (sq_prepare (dbh, - "DELETE FROM ds080 " - "WHERE key=? AND vhash=? AND type=? AND size=?", - &dstmt) != SQLITE_OK)) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh)); - GNUNET_GE_BREAK (NULL, 0); - if (dstmt != NULL) - sqlite3_finalize (dstmt); - if (stmt != NULL) - sqlite3_finalize (stmt); - return GNUNET_SYSERR; - } - err = SQLITE_DONE; - while ((payload * 10 > quota * 9) && /* we seem to be about 10% off */ - ((err = sqlite3_step (stmt)) == SQLITE_ROW)) - { - dsize = sqlite3_column_int (stmt, 0); - dtype = sqlite3_column_int (stmt, 1); - GNUNET_GE_BREAK (NULL, - sqlite3_column_bytes (stmt, - 2) == sizeof (GNUNET_HashCode)); - GNUNET_GE_BREAK (NULL, - sqlite3_column_bytes (stmt, - 3) == sizeof (GNUNET_HashCode)); - memcpy (&dkey, sqlite3_column_blob (stmt, 2), sizeof (GNUNET_HashCode)); - memcpy (&vhash, sqlite3_column_blob (stmt, 3), - sizeof (GNUNET_HashCode)); - sqlite3_reset (stmt); - sqlite3_bind_blob (dstmt, - 1, &dkey, sizeof (GNUNET_HashCode), - SQLITE_TRANSIENT); - sqlite3_bind_blob (dstmt, - 2, &vhash, sizeof (GNUNET_HashCode), - SQLITE_TRANSIENT); - sqlite3_bind_int (dstmt, 3, dtype); - sqlite3_bind_int (dstmt, 4, dsize); - if ((err = sqlite3_step (dstmt)) != SQLITE_DONE) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sqlite3_step", __FILE__, __LINE__, - sqlite3_errmsg (dbh)); - sqlite3_reset (dstmt); - GNUNET_GE_BREAK (NULL, 0); /* should delete but cannot!? */ - break; - } - if (sqlite3_total_changes (dbh) > 0) - { - if (bloom != NULL) - GNUNET_bloomfilter_remove (bloom, &dkey); - payload -= (dsize + OVERHEAD); - } -#if DEBUG_DSTORE - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | - GNUNET_GE_DEVELOPER, - "Deleting %u bytes decreases DStore payload to %llu out of %llu\n", - dsize, payload, quota); -#endif - sqlite3_reset (dstmt); - } - if (err != SQLITE_DONE) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sqlite3_step", __FILE__, __LINE__, - sqlite3_errmsg (dbh)); - } - sqlite3_finalize (dstmt); - sqlite3_finalize (stmt); - if (payload * 10 > quota * 9) - { - /* we seem to be about 10% off */ - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_DEVELOPER, - "Failed to delete content to drop below quota (bug?).\n", - payload, quota); - return GNUNET_SYSERR; - } + if (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC LIMIT 1", + &stmt)) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sq_prepare"); + if (stmt != NULL) + (void) sqlite3_finalize (stmt); + return GNUNET_SYSERR; + } + if (SQLITE_ROW != sqlite3_step (stmt)) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + (void) sqlite3_finalize (stmt); + return GNUNET_SYSERR; + } + rowid = sqlite3_column_int64 (stmt, 0); + GNUNET_assert (sqlite3_column_bytes (stmt, 1) == sizeof (struct GNUNET_HashCode)); + GNUNET_memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (struct GNUNET_HashCode)); + dsize = sqlite3_column_bytes (stmt, 2); + if (SQLITE_OK != sqlite3_finalize (stmt)) + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + if (SQLITE_OK != + sq_prepare (plugin->dbh, + "DELETE FROM ds090 WHERE _ROWID_=?", &dstmt)) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sq_prepare"); + if (stmt != NULL) + (void) sqlite3_finalize (stmt); + return GNUNET_SYSERR; + } + if (SQLITE_OK != sqlite3_bind_int64 (dstmt, 1, rowid)) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind"); + (void) sqlite3_finalize (dstmt); + return GNUNET_SYSERR; + } + if (SQLITE_DONE != sqlite3_step (dstmt)) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_step"); + (void) sqlite3_finalize (dstmt); + return GNUNET_SYSERR; + } + plugin->num_items--; + plugin->env->delete_notify (plugin->env->cls, + &hc, + dsize + OVERHEAD); + if (SQLITE_OK != sqlite3_finalize (dstmt)) + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_finalize"); return GNUNET_OK; } + /** - * Store an item in the datastore. + * Obtain a random key-value pair from the datacache. * - * @return GNUNET_OK on success, GNUNET_SYSERR on error + * @param cls closure (our `struct Plugin`) + * @param iter maybe NULL (to just count) + * @param iter_cls closure for @a iter + * @return the number of results found, zero (datacache empty) or one */ -static int -d_put (const GNUNET_HashCode * key, - unsigned int type, - GNUNET_CronTime discard_time, unsigned int size, const char *data) +static unsigned int +sqlite_plugin_get_random (void *cls, + GNUNET_DATACACHE_Iterator iter, + void *iter_cls) { - GNUNET_HashCode vhash; - sqlite3 *dbh; + struct Plugin *plugin = cls; sqlite3_stmt *stmt; - int ret; - GNUNET_CronTime now; - - if (size > MAX_CONTENT_SIZE) - return GNUNET_SYSERR; - GNUNET_hash (data, size, &vhash); - GNUNET_mutex_lock (lock); - if ((fn == NULL) || (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))) - { - db_reset (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; - } - now = GNUNET_get_time (); -#if DEBUG_DSTORE - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER, - "dstore processes put `%.*s' with expiration %llu\n", - size, data, discard_time); -#endif - - /* first try UPDATE */ - if (sq_prepare (dbh, - "UPDATE ds080 SET puttime=?, expire=? " - "WHERE key=? AND vhash=? AND type=? AND size=?", - &stmt) != SQLITE_OK) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh)); - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; - } - if ((SQLITE_OK != - sqlite3_bind_int64 (stmt, 1, now)) || - (SQLITE_OK != - sqlite3_bind_int64 (stmt, 2, discard_time)) || - (SQLITE_OK != - sqlite3_bind_blob (stmt, 3, key, sizeof (GNUNET_HashCode), - SQLITE_TRANSIENT)) || - (SQLITE_OK != - sqlite3_bind_blob (stmt, 4, &vhash, sizeof (GNUNET_HashCode), - SQLITE_TRANSIENT)) || - (SQLITE_OK != sqlite3_bind_int (stmt, 5, type)) || - (SQLITE_OK != sqlite3_bind_int (stmt, 6, size))) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sqlite3_bind_xxx", __FILE__, __LINE__, - sqlite3_errmsg (dbh)); - sqlite3_finalize (stmt); - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; - } - if (SQLITE_DONE != sqlite3_step (stmt)) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sqlite3_step", __FILE__, __LINE__, - sqlite3_errmsg (dbh)); - sqlite3_finalize (stmt); - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; - } - ret = sqlite3_changes (dbh); - sqlite3_finalize (stmt); - if (ret > 0) - { - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_OK; - } - if (GNUNET_OK != checkQuota (dbh)) - { - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; - } - if (sq_prepare (dbh, - "INSERT INTO ds080 " - "(size, type, puttime, expire, key, vhash, value) " - "VALUES (?, ?, ?, ?, ?, ?, ?)", &stmt) != SQLITE_OK) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh)); - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; - } - if ((SQLITE_OK == sqlite3_bind_int (stmt, 1, size)) && - (SQLITE_OK == sqlite3_bind_int (stmt, 2, type)) && - (SQLITE_OK == sqlite3_bind_int64 (stmt, 3, now)) && - (SQLITE_OK == sqlite3_bind_int64 (stmt, 4, discard_time)) && - (SQLITE_OK == - sqlite3_bind_blob (stmt, 5, key, sizeof (GNUNET_HashCode), - SQLITE_TRANSIENT)) && - (SQLITE_OK == - sqlite3_bind_blob (stmt, 6, &vhash, sizeof (GNUNET_HashCode), - SQLITE_TRANSIENT)) - && (SQLITE_OK == - sqlite3_bind_blob (stmt, 7, data, size, SQLITE_TRANSIENT))) - { - if (SQLITE_DONE != sqlite3_step (stmt)) - { - LOG_SQLITE (dbh, - GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN - | GNUNET_GE_BULK, "sqlite3_step"); - } - else - { - payload += size + OVERHEAD; - if (bloom != NULL) - GNUNET_bloomfilter_add (bloom, key); - } - if (SQLITE_OK != sqlite3_finalize (stmt)) - LOG_SQLITE (dbh, - GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN | - GNUNET_GE_BULK, "sqlite3_finalize"); - } + struct GNUNET_TIME_Absolute exp; + unsigned int size; + const char *dat; + unsigned int off; + unsigned int psize; + unsigned int type; + char scratch[256]; + int64_t ntime; + const struct GNUNET_PeerIdentity *path; + const struct GNUNET_HashCode *key; + + if (0 == plugin->num_items) + return 0; + if (NULL == iter) + return 1; + off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + plugin->num_items); + GNUNET_snprintf (scratch, + sizeof (scratch), + "SELECT value,expire,path,key,type FROM ds090 ORDER BY key LIMIT 1 OFFSET %u", + off); + if (SQLITE_OK != + sq_prepare (plugin->dbh, scratch, &stmt)) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sq_prepare"); + return 0; + } + if (SQLITE_ROW != sqlite3_step (stmt)) + { + GNUNET_break (0); + sqlite3_finalize (stmt); + return 0; + } + size = sqlite3_column_bytes (stmt, 0); + dat = sqlite3_column_blob (stmt, 0); + exp.abs_value_us = sqlite3_column_int64 (stmt, 1); + psize = sqlite3_column_bytes (stmt, 2); + if (0 != psize % sizeof (struct GNUNET_PeerIdentity)) + { + GNUNET_break (0); + psize = 0; + } + psize /= sizeof (struct GNUNET_PeerIdentity); + if (0 != psize) + path = sqlite3_column_blob (stmt, 2); else - { - LOG_SQLITE (dbh, - GNUNET_GE_ERROR | GNUNET_GE_DEVELOPER | GNUNET_GE_ADMIN | - GNUNET_GE_BULK, "sqlite3_bind_xxx"); - } -#if DEBUG_DSTORE - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER, - "Storing %u bytes increases DStore payload to %llu out of %llu\n", - size, payload, quota); -#endif - checkQuota (dbh); - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - if (stats != NULL) - stats->set (stat_dstore_size, payload); - return GNUNET_OK; + path = NULL; + + GNUNET_assert (sizeof (struct GNUNET_HashCode) == + sqlite3_column_bytes (stmt, 3)); + key = sqlite3_column_blob (stmt, 3); + type = sqlite3_column_int (stmt, 4); + + ntime = (int64_t) exp.abs_value_us; + if (ntime == INT64_MAX) + exp = GNUNET_TIME_UNIT_FOREVER_ABS; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Found %u-byte result with key %s when processing GET-RANDOM\n", + (unsigned int) size, + GNUNET_h2s (key)); + (void) iter (iter_cls, + key, + size, + dat, + type, + exp, + psize, + path); + sqlite3_finalize (stmt); + return 1; } + /** - * Iterate over the results for a particular key - * in the datastore. + * Iterate over the results that are "close" to a particular key in + * the datacache. "close" is defined as numerically larger than @a + * key (when interpreted as a circular address space), with small + * distance. * - * @param key - * @param type entries of which type are relevant? + * @param cls closure (internal context for the plugin) + * @param key area of the keyspace to look into + * @param num_results number of results that should be returned to @a iter * @param iter maybe NULL (to just count) - * @return the number of results, GNUNET_SYSERR if the - * iter is non-NULL and aborted the iteration + * @param iter_cls closure for @a iter + * @return the number of results found */ -static int -d_get (const GNUNET_HashCode * key, - unsigned int type, GNUNET_ResultProcessor handler, void *closure) +static unsigned int +sqlite_plugin_get_closest (void *cls, + const struct GNUNET_HashCode *key, + unsigned int num_results, + GNUNET_DATACACHE_Iterator iter, + void *iter_cls) { - sqlite3 *dbh; + struct Plugin *plugin = cls; sqlite3_stmt *stmt; - GNUNET_CronTime now; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute exp; unsigned int size; const char *dat; unsigned int cnt; - unsigned int off; - unsigned int total; - char scratch[256]; - - GNUNET_mutex_lock (lock); - if ((bloom != NULL) && (GNUNET_NO == GNUNET_bloomfilter_test (bloom, key))) - { - GNUNET_mutex_unlock (lock); - return 0; - } - if ((fn == NULL) || (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))) + unsigned int psize; + unsigned int type; + int64_t ntime; + const struct GNUNET_PeerIdentity *path; + + now = GNUNET_TIME_absolute_get (); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing GET_CLOSEST for key `%4s'\n", + GNUNET_h2s (key)); + if (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT value,expire,path,type,key FROM ds090 WHERE key>=? AND expire >= ? ORDER BY KEY ASC LIMIT ?", + &stmt)) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sq_prepare"); + return 0; + } + ntime = (int64_t) now.abs_value_us; + GNUNET_assert (ntime >= 0); + if ((SQLITE_OK != + sqlite3_bind_blob (stmt, + 1, + key, + sizeof (struct GNUNET_HashCode), + SQLITE_TRANSIENT)) || + (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, now.abs_value_us)) || + (SQLITE_OK != sqlite3_bind_int (stmt, 3, num_results)) ) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite3_bind_xxx"); + sqlite3_finalize (stmt); + return 0; + } + cnt = 0; + while (SQLITE_ROW == sqlite3_step (stmt)) + { + if (sizeof (struct GNUNET_HashCode) != + sqlite3_column_bytes (stmt, 4)) { - db_reset (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; + GNUNET_break (0); + break; } - now = GNUNET_get_time (); -#if DEBUG_DSTORE - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_DEVELOPER, - "dstore processes get at `%llu'\n", now); -#endif - if (sq_prepare (dbh, - "SELECT count(*) FROM ds080 WHERE key=? AND type=? AND expire >= ?", - &stmt) != SQLITE_OK) + size = sqlite3_column_bytes (stmt, 0); + dat = sqlite3_column_blob (stmt, 0); + exp.abs_value_us = sqlite3_column_int64 (stmt, 1); + psize = sqlite3_column_bytes (stmt, 2); + type = sqlite3_column_int (stmt, 3); + key = sqlite3_column_blob (stmt, 4); + if (0 != psize % sizeof (struct GNUNET_PeerIdentity)) { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sq_prepare", __FILE__, __LINE__, sqlite3_errmsg (dbh)); - sqlite3_close (dbh); - db_reset (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; + GNUNET_break (0); + psize = 0; } - sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode), - SQLITE_TRANSIENT); - sqlite3_bind_int (stmt, 2, type); - sqlite3_bind_int64 (stmt, 3, now); - if (SQLITE_ROW != sqlite3_step (stmt)) + psize /= sizeof (struct GNUNET_PeerIdentity); + if (0 != psize) + path = sqlite3_column_blob (stmt, 2); + else + path = NULL; + ntime = (int64_t) exp.abs_value_us; + if (ntime == INT64_MAX) + exp = GNUNET_TIME_UNIT_FOREVER_ABS; + cnt++; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Found %u-byte result at %s when processing GET_CLOSE\n", + (unsigned int) size, + GNUNET_h2s (key)); + if (GNUNET_OK != iter (iter_cls, + key, + size, + dat, + type, + exp, + psize, + path)) { - LOG_SQLITE (dbh, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_USER | - GNUNET_GE_BULK, "sqlite_step"); - sqlite3_reset (stmt); sqlite3_finalize (stmt); - db_reset (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; + break; } - total = sqlite3_column_int (stmt, 0); - sqlite3_reset (stmt); + } sqlite3_finalize (stmt); - if ((total == 0) || (handler == NULL)) - { - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - return total; - } - - cnt = 0; - off = GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, total); - while (cnt < total) - { - off = (off + 1) % total; - GNUNET_snprintf (scratch, 256, - "SELECT size, value FROM ds080 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u", - off); - if (sq_prepare (dbh, scratch, &stmt) != SQLITE_OK) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | GNUNET_GE_BULK, - _("`%s' failed at %s:%d with error: %s\n"), - "sq_prepare", __FILE__, __LINE__, - sqlite3_errmsg (dbh)); - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); - return GNUNET_SYSERR; - } - sqlite3_bind_blob (stmt, 1, key, sizeof (GNUNET_HashCode), - SQLITE_TRANSIENT); - sqlite3_bind_int (stmt, 2, type); - sqlite3_bind_int64 (stmt, 3, now); - if (sqlite3_step (stmt) != SQLITE_ROW) - break; - size = sqlite3_column_int (stmt, 0); - if (size != sqlite3_column_bytes (stmt, 1)) - { - GNUNET_GE_BREAK (NULL, 0); - sqlite3_finalize (stmt); - continue; - } - dat = sqlite3_column_blob (stmt, 1); - cnt++; -#if DEBUG_DSTORE - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | - GNUNET_GE_DEVELOPER, - "dstore found result for get: `%.*s'\n", size, dat); -#endif - if ((handler != NULL) && - (GNUNET_OK != handler (key, type, size, dat, closure))) - { - sqlite3_finalize (stmt); - break; - } - sqlite3_finalize (stmt); - } - sqlite3_close (dbh); - GNUNET_mutex_unlock (lock); return cnt; } -GNUNET_Dstore_ServiceAPI * -provide_module_dstore_sqlite (GNUNET_CoreAPIForPlugins * capi) + +/** + * Entry point for the plugin. + * + * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironment`) + * @return the plugin's closure (our `struct Plugin`) + */ +void * +libgnunet_plugin_datacache_sqlite_init (void *cls) { - static GNUNET_Dstore_ServiceAPI api; - int fd; + struct GNUNET_DATACACHE_PluginEnvironment *env = cls; + struct GNUNET_DATACACHE_PluginFunctions *api; + struct Plugin *plugin; + char *fn; + char *fn_utf8; + sqlite3 *dbh; + char *emsg; -#if DEBUG_SQLITE - GNUNET_GE_LOG (capi->ectx, - GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, - "SQLite Dstore: initializing database\n"); -#endif - coreAPI = capi; - if (GNUNET_OK != db_reset ()) + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (env->cfg, + "datacache-sqlite", + "IN_MEMORY")) + { + if (SQLITE_OK != sqlite3_open (":memory:", &dbh)) + return NULL; + fn_utf8 = NULL; + } + else + { + fn = GNUNET_DISK_mktemp ("gnunet-datacache"); + if (fn == NULL) + { + GNUNET_break (0); + return NULL; + } + /* fn should be UTF-8-encoded. If it isn't, it's a bug. */ + fn_utf8 = GNUNET_strdup (fn); + if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh)) { - GNUNET_GE_BREAK (capi->ectx, 0); + GNUNET_free (fn); + GNUNET_free (fn_utf8); return NULL; } - lock = GNUNET_mutex_create (GNUNET_NO); - + GNUNET_free (fn); + } - api.get = &d_get; - api.put = &d_put; - GNUNET_GC_get_configuration_value_number (coreAPI->cfg, - "DSTORE", "QUOTA", 1, 1024, 1, - "a); - if (quota == 0) /* error */ - quota = 1; - quota *= 1024 * 1024; + SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY"); + SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE"); + SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF"); + SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF"); + SQLITE3_EXEC (dbh, "PRAGMA page_size=4092"); + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (env->cfg, + "datacache-sqlite", + "IN_MEMORY")) + SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3"); - bloom_name = GNUNET_strdup ("/tmp/dbloomXXXXXX"); - fd = mkstemp (bloom_name); - if (fd != -1) - { - bloom = GNUNET_bloomfilter_load (coreAPI->ectx, bloom_name, quota / (OVERHEAD + 1024), /* 8 bit per entry in DB, expect 1k entries */ - 5); - CLOSE (fd); - } - stats = capi->service_request ("stats"); - if (stats != NULL) - { - stat_dstore_size = stats->create (gettext_noop ("# bytes in dstore")); - stat_dstore_quota = - stats->create (gettext_noop ("# max bytes allowed in dstore")); - stats->set (stat_dstore_quota, quota); - } - return &api; + SQLITE3_EXEC (dbh, + "CREATE TABLE ds090 (" " type INTEGER NOT NULL DEFAULT 0," + " expire INTEGER NOT NULL DEFAULT 0," + " key BLOB NOT NULL DEFAULT ''," + " value BLOB NOT NULL DEFAULT ''," + " path BLOB DEFAULT '')"); + SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds090 (key,type,expire)"); + SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire ON ds090 (expire)"); + plugin = GNUNET_new (struct Plugin); + plugin->env = env; + plugin->dbh = dbh; + plugin->fn = fn_utf8; + api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions); + api->cls = plugin; + api->get = &sqlite_plugin_get; + api->put = &sqlite_plugin_put; + api->del = &sqlite_plugin_del; + api->get_random = &sqlite_plugin_get_random; + api->get_closest = &sqlite_plugin_get_closest; + LOG (GNUNET_ERROR_TYPE_INFO, + "Sqlite datacache running\n"); + return api; } + /** - * Shutdown the module. + * Exit point from the plugin. + * + * @param cls closure (our `struct Plugin`) + * @return NULL */ -void -release_module_dstore_sqlite () +void * +libgnunet_plugin_datacache_sqlite_done (void *cls) { - UNLINK (fn); - GNUNET_free (fn); - GNUNET_free (fn_utf8); - fn = NULL; - if (bloom != NULL) - { - GNUNET_bloomfilter_free (bloom); - bloom = NULL; - } - UNLINK (bloom_name); - GNUNET_free (bloom_name); - bloom_name = NULL; - if (stats != NULL) + struct GNUNET_DATACACHE_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + int result; + +#if SQLITE_VERSION_NUMBER >= 3007000 + sqlite3_stmt *stmt; +#endif + +#if !WINDOWS || defined(__CYGWIN__) + if ( (NULL != plugin->fn) && + (0 != UNLINK (plugin->fn)) ) + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "unlink", + plugin->fn); + GNUNET_free_non_null (plugin->fn); +#endif + result = sqlite3_close (plugin->dbh); +#if SQLITE_VERSION_NUMBER >= 3007000 + if (SQLITE_BUSY == result) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Tried to close sqlite without finalizing all prepared statements.\n")); + stmt = sqlite3_next_stmt (plugin->dbh, NULL); + while (NULL != stmt) { - coreAPI->service_release (stats); - stats = NULL; + result = sqlite3_finalize (stmt); + if (result != SQLITE_OK) + LOG (GNUNET_ERROR_TYPE_WARNING, + "Failed to close statement %p: %d\n", + stmt, + result); + stmt = sqlite3_next_stmt (plugin->dbh, NULL); } -#if DEBUG_SQLITE - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER, - "SQLite Dstore: database shutdown\n"); + result = sqlite3_close (plugin->dbh); + } #endif - GNUNET_mutex_destroy (lock); - coreAPI = NULL; + if (SQLITE_OK != result) + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR, + "sqlite3_close"); + +#if WINDOWS && !defined(__CYGWIN__) + if ( (NULL != plugin->fn) && + (0 != UNLINK (plugin->fn)) ) + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, + "unlink", + plugin->fn); + GNUNET_free_non_null (plugin->fn); +#endif + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; } -/* end of dstore.c */ + + +/* end of plugin_datacache_sqlite.c */