2 This file is part of GNUnet
3 Copyright (C) 2006, 2009, 2015 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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"
29 #include "gnunet_sq_lib.h"
32 #define LOG(kind, ...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__)
34 #define LOG_STRERROR_FILE(kind, op, fn) \
35 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(struct GNUNET_HashCode) + 36)
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.
65 * Prepared statement for #sqlite_plugin_put.
67 sqlite3_stmt *insert_stmt;
70 * Prepared statement for #sqlite_plugin_get.
72 sqlite3_stmt *get_count_stmt;
75 * Prepared statement for #sqlite_plugin_get.
77 sqlite3_stmt *get_stmt;
80 * Prepared statement for #sqlite_plugin_del.
82 sqlite3_stmt *del_select_stmt;
85 * Prepared statement for #sqlite_plugin_del.
87 sqlite3_stmt *del_expired_stmt;
90 * Prepared statement for #sqlite_plugin_del.
92 sqlite3_stmt *del_stmt;
95 * Prepared statement for #sqlite_plugin_get_random.
97 sqlite3_stmt *get_random_stmt;
100 * Prepared statement for #sqlite_plugin_get_closest.
102 sqlite3_stmt *get_closest_stmt;
105 * Number of key-value pairs in the database.
107 unsigned int num_items;
112 * Log an error message at log-level @a level that indicates
113 * a failure of the command @a cmd with the error from the database @a db
115 * @param db database handle
116 * @param level log level
117 * @param cmd failed command
119 #define LOG_SQLITE(db, level, cmd) \
123 _ ("`%s' failed at %s:%d with error: %s\n"), \
127 sqlite3_errmsg (db)); \
132 * Execute SQL statement.
134 * @param db database handle
135 * @param cmd SQL command to execute
137 #define SQLITE3_EXEC(db, cmd) \
141 if (SQLITE_OK != sqlite3_exec (db, cmd, NULL, NULL, &emsg)) \
143 LOG (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, \
144 _ ("`%s' failed at %s:%d with error: %s\n"), \
149 sqlite3_free (emsg); \
155 * @brief Prepare a SQL statement
157 * @param dbh database handle
158 * @param zsql SQL statement text
159 * @param[out] ppStmt set to the prepared statement
160 * @return 0 on success
163 sq_prepare (sqlite3 *dbh,
164 const char *zSql, /* SQL statement, UTF-8 encoded */
165 sqlite3_stmt **ppStmt)
166 { /* OUT: Statement handle */
169 return sqlite3_prepare (dbh,
173 (const char **) &dummy);
178 * Store an item in the datastore.
180 * @param cls closure (our `struct Plugin`)
181 * @param key key to store @a data under
182 * @param xor_distance how close is @a key to our PID?
183 * @param size number of bytes in @a data
184 * @param data data to store
185 * @param type type of the value
186 * @param discard_time when to discard the value in any case
187 * @param path_info_len number of entries in @a path_info
188 * @param path_info array of peers that have processed the request
189 * @return 0 if duplicate, -1 on error, number of bytes used otherwise
192 sqlite_plugin_put (void *cls,
193 const struct GNUNET_HashCode *key,
194 uint32_t xor_distance,
197 enum GNUNET_BLOCK_Type type,
198 struct GNUNET_TIME_Absolute discard_time,
199 unsigned int path_info_len,
200 const struct GNUNET_PeerIdentity *path_info)
202 struct Plugin *plugin = cls;
203 uint32_t type32 = type;
204 struct GNUNET_SQ_QueryParam params[] =
205 { GNUNET_SQ_query_param_uint32 (&type32),
206 GNUNET_SQ_query_param_absolute_time (&discard_time),
207 GNUNET_SQ_query_param_auto_from_type (key),
208 GNUNET_SQ_query_param_uint32 (&xor_distance),
209 GNUNET_SQ_query_param_fixed_size (data, size),
210 GNUNET_SQ_query_param_fixed_size (path_info,
212 * sizeof(struct GNUNET_PeerIdentity)),
213 GNUNET_SQ_query_param_end };
215 LOG (GNUNET_ERROR_TYPE_DEBUG,
216 "Processing PUT of %u bytes with key `%s' and expiration %s\n",
219 GNUNET_STRINGS_relative_time_to_string (
220 GNUNET_TIME_absolute_get_remaining (
223 if (GNUNET_OK != GNUNET_SQ_bind (plugin->insert_stmt, params))
225 LOG_SQLITE (plugin->dbh,
226 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
228 GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
231 if (SQLITE_DONE != sqlite3_step (plugin->insert_stmt))
233 LOG_SQLITE (plugin->dbh,
234 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
236 GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
240 GNUNET_SQ_reset (plugin->dbh, plugin->insert_stmt);
241 return size + OVERHEAD;
246 * Iterate over the results for a particular key
249 * @param cls closure (our `struct Plugin`)
251 * @param type entries of which type are relevant?
252 * @param iter maybe NULL (to just count)
253 * @param iter_cls closure for @a iter
254 * @return the number of results found
257 sqlite_plugin_get (void *cls,
258 const struct GNUNET_HashCode *key,
259 enum GNUNET_BLOCK_Type type,
260 GNUNET_DATACACHE_Iterator iter,
263 struct Plugin *plugin = cls;
264 uint32_t type32 = type;
265 struct GNUNET_TIME_Absolute now;
266 struct GNUNET_TIME_Absolute exp;
273 struct GNUNET_PeerIdentity *path;
274 struct GNUNET_SQ_QueryParam params_count[] =
275 { GNUNET_SQ_query_param_auto_from_type (key),
276 GNUNET_SQ_query_param_uint32 (&type32),
277 GNUNET_SQ_query_param_absolute_time (&now),
278 GNUNET_SQ_query_param_end };
279 struct GNUNET_SQ_QueryParam params_select[] =
280 { GNUNET_SQ_query_param_auto_from_type (key),
281 GNUNET_SQ_query_param_uint32 (&type32),
282 GNUNET_SQ_query_param_absolute_time (&now),
283 GNUNET_SQ_query_param_uint32 (&off),
284 GNUNET_SQ_query_param_end };
285 struct GNUNET_SQ_ResultSpec rs[] =
286 { GNUNET_SQ_result_spec_variable_size (&dat, &size),
287 GNUNET_SQ_result_spec_absolute_time (&exp),
288 GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
289 GNUNET_SQ_result_spec_end };
291 now = GNUNET_TIME_absolute_get ();
292 LOG (GNUNET_ERROR_TYPE_DEBUG,
293 "Processing GET for key `%s'\n",
296 if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_count_stmt, params_count))
298 LOG_SQLITE (plugin->dbh,
299 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
301 GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
304 if (SQLITE_ROW != sqlite3_step (plugin->get_count_stmt))
306 LOG_SQLITE (plugin->dbh,
307 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
309 GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
310 LOG (GNUNET_ERROR_TYPE_DEBUG,
311 "No content found when processing GET for key `%s'\n",
315 total = sqlite3_column_int (plugin->get_count_stmt, 0);
316 GNUNET_SQ_reset (plugin->dbh, plugin->get_count_stmt);
317 if ((0 == total) || (NULL == iter))
320 LOG (GNUNET_ERROR_TYPE_DEBUG,
321 "No content found when processing GET for key `%s'\n",
327 off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total);
330 off = (off + 1) % total;
331 if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_stmt, params_select))
333 LOG_SQLITE (plugin->dbh,
334 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
336 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
339 if (SQLITE_ROW != sqlite3_step (plugin->get_stmt))
341 if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_stmt, rs))
344 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
347 if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
353 psize /= sizeof(struct GNUNET_PeerIdentity);
355 LOG (GNUNET_ERROR_TYPE_DEBUG,
356 "Found %u-byte result when processing GET for key `%s'\n",
359 if (GNUNET_OK != iter (iter_cls, key, size, dat, type, exp, psize, path))
361 GNUNET_SQ_cleanup_result (rs);
362 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
365 GNUNET_SQ_cleanup_result (rs);
366 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
368 GNUNET_SQ_reset (plugin->dbh, plugin->get_stmt);
374 * Delete the entry with the lowest expiration value
375 * from the datacache right now.
377 * @param cls closure (our `struct Plugin`)
378 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
381 sqlite_plugin_del (void *cls)
383 struct Plugin *plugin = cls;
387 struct GNUNET_HashCode hc;
388 struct GNUNET_TIME_Absolute now;
389 struct GNUNET_SQ_ResultSpec rs[] =
390 { GNUNET_SQ_result_spec_uint64 (&rowid),
391 GNUNET_SQ_result_spec_auto_from_type (&hc),
392 GNUNET_SQ_result_spec_variable_size ((void **) &data, &dsize),
393 GNUNET_SQ_result_spec_end };
394 struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint64 (
396 GNUNET_SQ_query_param_end };
397 struct GNUNET_SQ_QueryParam time_params[] =
398 { GNUNET_SQ_query_param_absolute_time (&now), GNUNET_SQ_query_param_end };
400 LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing DEL\n");
401 now = GNUNET_TIME_absolute_get ();
402 if (GNUNET_OK != GNUNET_SQ_bind (plugin->del_expired_stmt, time_params))
404 LOG_SQLITE (plugin->dbh,
405 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
407 GNUNET_SQ_reset (plugin->dbh, plugin->del_expired_stmt);
408 return GNUNET_SYSERR;
410 if ((SQLITE_ROW != sqlite3_step (plugin->del_expired_stmt)) ||
411 (GNUNET_OK != GNUNET_SQ_extract_result (plugin->del_expired_stmt, rs)))
413 GNUNET_SQ_reset (plugin->dbh, plugin->del_expired_stmt);
414 if (SQLITE_ROW != sqlite3_step (plugin->del_select_stmt))
416 LOG_SQLITE (plugin->dbh,
417 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
419 GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
420 return GNUNET_SYSERR;
422 if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->del_select_stmt, rs))
424 GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
426 return GNUNET_SYSERR;
429 GNUNET_SQ_cleanup_result (rs);
430 GNUNET_SQ_reset (plugin->dbh, plugin->del_select_stmt);
431 if (GNUNET_OK != GNUNET_SQ_bind (plugin->del_stmt, params))
433 LOG_SQLITE (plugin->dbh,
434 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
436 GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
437 return GNUNET_SYSERR;
439 if (SQLITE_DONE != sqlite3_step (plugin->del_stmt))
441 LOG_SQLITE (plugin->dbh,
442 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
444 GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
445 return GNUNET_SYSERR;
448 plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD);
449 GNUNET_SQ_reset (plugin->dbh, plugin->del_stmt);
455 * Obtain a random key-value pair from the datacache.
457 * @param cls closure (our `struct Plugin`)
458 * @param iter maybe NULL (to just count)
459 * @param iter_cls closure for @a iter
460 * @return the number of results found, zero (datacache empty) or one
463 sqlite_plugin_get_random (void *cls,
464 GNUNET_DATACACHE_Iterator iter,
467 struct Plugin *plugin = cls;
468 struct GNUNET_TIME_Absolute exp;
474 struct GNUNET_PeerIdentity *path;
475 struct GNUNET_HashCode key;
476 struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint32 (&off),
477 GNUNET_SQ_query_param_end };
478 struct GNUNET_SQ_ResultSpec rs[] =
479 { GNUNET_SQ_result_spec_variable_size (&dat, &size),
480 GNUNET_SQ_result_spec_absolute_time (&exp),
481 GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
482 GNUNET_SQ_result_spec_auto_from_type (&key),
483 GNUNET_SQ_result_spec_uint32 (&type),
484 GNUNET_SQ_result_spec_end };
486 if (0 == plugin->num_items)
491 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, plugin->num_items);
492 if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_random_stmt, params))
496 if (SQLITE_ROW != sqlite3_step (plugin->get_random_stmt))
499 GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
502 if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_random_stmt, rs))
505 GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
508 if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
514 psize /= sizeof(struct GNUNET_PeerIdentity);
515 LOG (GNUNET_ERROR_TYPE_DEBUG,
516 "Found %u-byte result with key %s when processing GET-RANDOM\n",
519 (void) iter (iter_cls,
523 (enum GNUNET_BLOCK_Type) type,
527 GNUNET_SQ_cleanup_result (rs);
528 GNUNET_SQ_reset (plugin->dbh, plugin->get_random_stmt);
534 * Iterate over the results that are "close" to a particular key in
535 * the datacache. "close" is defined as numerically larger than @a
536 * key (when interpreted as a circular address space), with small
539 * @param cls closure (internal context for the plugin)
540 * @param key area of the keyspace to look into
541 * @param num_results number of results that should be returned to @a iter
542 * @param iter maybe NULL (to just count)
543 * @param iter_cls closure for @a iter
544 * @return the number of results found
547 sqlite_plugin_get_closest (void *cls,
548 const struct GNUNET_HashCode *key,
549 unsigned int num_results,
550 GNUNET_DATACACHE_Iterator iter,
553 struct Plugin *plugin = cls;
554 uint32_t num_results32 = num_results;
555 struct GNUNET_TIME_Absolute now;
556 struct GNUNET_TIME_Absolute exp;
562 struct GNUNET_HashCode hc;
563 struct GNUNET_PeerIdentity *path;
564 struct GNUNET_SQ_QueryParam params[] =
565 { GNUNET_SQ_query_param_auto_from_type (key),
566 GNUNET_SQ_query_param_absolute_time (&now),
567 GNUNET_SQ_query_param_uint32 (&num_results32),
568 GNUNET_SQ_query_param_end };
569 struct GNUNET_SQ_ResultSpec rs[] =
570 { GNUNET_SQ_result_spec_variable_size (&dat, &size),
571 GNUNET_SQ_result_spec_absolute_time (&exp),
572 GNUNET_SQ_result_spec_variable_size ((void **) &path, &psize),
573 GNUNET_SQ_result_spec_uint32 (&type),
574 GNUNET_SQ_result_spec_auto_from_type (&hc),
575 GNUNET_SQ_result_spec_end };
577 now = GNUNET_TIME_absolute_get ();
578 LOG (GNUNET_ERROR_TYPE_DEBUG,
579 "Processing GET_CLOSEST for key `%s'\n",
581 if (GNUNET_OK != GNUNET_SQ_bind (plugin->get_closest_stmt, params))
583 LOG_SQLITE (plugin->dbh,
584 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
586 GNUNET_SQ_reset (plugin->dbh, plugin->get_closest_stmt);
590 while (SQLITE_ROW == sqlite3_step (plugin->get_closest_stmt))
592 if (GNUNET_OK != GNUNET_SQ_extract_result (plugin->get_closest_stmt, rs))
597 if (0 != psize % sizeof(struct GNUNET_PeerIdentity))
603 psize /= sizeof(struct GNUNET_PeerIdentity);
605 LOG (GNUNET_ERROR_TYPE_DEBUG,
606 "Found %u-byte result at %s when processing GET_CLOSE\n",
609 if (GNUNET_OK != iter (iter_cls, &hc, size, dat, type, exp, psize, path))
611 GNUNET_SQ_cleanup_result (rs);
614 GNUNET_SQ_cleanup_result (rs);
616 GNUNET_SQ_reset (plugin->dbh, plugin->get_closest_stmt);
622 * Entry point for the plugin.
624 * @param cls closure (the `struct GNUNET_DATACACHE_PluginEnvironment`)
625 * @return the plugin's closure (our `struct Plugin`)
628 libgnunet_plugin_datacache_sqlite_init (void *cls)
630 struct GNUNET_DATACACHE_PluginEnvironment *env = cls;
631 struct GNUNET_DATACACHE_PluginFunctions *api;
632 struct Plugin *plugin;
638 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
642 if (SQLITE_OK != sqlite3_open (":memory:", &dbh))
648 fn = GNUNET_DISK_mktemp ("gnunet-datacache");
654 /* fn should be UTF-8-encoded. If it isn't, it's a bug. */
655 fn_utf8 = GNUNET_strdup (fn);
656 if (SQLITE_OK != sqlite3_open (fn_utf8, &dbh))
659 GNUNET_free (fn_utf8);
665 SQLITE3_EXEC (dbh, "PRAGMA temp_store=MEMORY");
666 SQLITE3_EXEC (dbh, "PRAGMA locking_mode=EXCLUSIVE");
667 SQLITE3_EXEC (dbh, "PRAGMA journal_mode=OFF");
668 SQLITE3_EXEC (dbh, "PRAGMA synchronous=OFF");
669 SQLITE3_EXEC (dbh, "PRAGMA page_size=4092");
670 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (env->cfg,
673 SQLITE3_EXEC (dbh, "PRAGMA sqlite_temp_store=3");
676 "CREATE TABLE ds091 ("
677 " type INTEGER NOT NULL DEFAULT 0,"
678 " expire INTEGER NOT NULL,"
679 " key BLOB NOT NULL DEFAULT '',"
680 " prox INTEGER NOT NULL,"
681 " value BLOB NOT NULL,"
682 " path BLOB DEFAULT '')");
683 SQLITE3_EXEC (dbh, "CREATE INDEX idx_hashidx ON ds091 (key,type,expire)");
684 SQLITE3_EXEC (dbh, "CREATE INDEX idx_prox_expire ON ds091 (prox,expire)");
685 SQLITE3_EXEC (dbh, "CREATE INDEX idx_expire_only ON ds091 (expire)");
686 plugin = GNUNET_new (struct Plugin);
689 plugin->fn = fn_utf8;
692 sq_prepare (plugin->dbh,
693 "INSERT INTO ds091 (type, expire, key, prox, value, path) "
694 "VALUES (?, ?, ?, ?, ?, ?)",
695 &plugin->insert_stmt)) ||
696 (SQLITE_OK != sq_prepare (plugin->dbh,
697 "SELECT count(*) FROM ds091 "
698 "WHERE key=? AND type=? AND expire >= ?",
699 &plugin->get_count_stmt)) ||
701 sq_prepare (plugin->dbh,
702 "SELECT value,expire,path FROM ds091"
703 " WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET ?",
704 &plugin->get_stmt)) ||
705 (SQLITE_OK != sq_prepare (plugin->dbh,
706 "SELECT _ROWID_,key,value FROM ds091"
708 " ORDER BY expire ASC LIMIT 1",
709 &plugin->del_expired_stmt)) ||
710 (SQLITE_OK != sq_prepare (plugin->dbh,
711 "SELECT _ROWID_,key,value FROM ds091"
712 " ORDER BY prox ASC, expire ASC LIMIT 1",
713 &plugin->del_select_stmt)) ||
714 (SQLITE_OK != sq_prepare (plugin->dbh,
715 "DELETE FROM ds091 WHERE _ROWID_=?",
716 &plugin->del_stmt)) ||
717 (SQLITE_OK != sq_prepare (plugin->dbh,
718 "SELECT value,expire,path,key,type FROM ds091 "
719 "ORDER BY key LIMIT 1 OFFSET ?",
720 &plugin->get_random_stmt)) ||
722 sq_prepare (plugin->dbh,
723 "SELECT value,expire,path,type,key FROM ds091 "
724 "WHERE key>=? AND expire >= ? ORDER BY KEY ASC LIMIT ?",
725 &plugin->get_closest_stmt)))
727 LOG_SQLITE (plugin->dbh,
728 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
730 GNUNET_break (SQLITE_OK == sqlite3_close (plugin->dbh));
731 GNUNET_free (plugin);
735 api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions);
737 api->get = &sqlite_plugin_get;
738 api->put = &sqlite_plugin_put;
739 api->del = &sqlite_plugin_del;
740 api->get_random = &sqlite_plugin_get_random;
741 api->get_closest = &sqlite_plugin_get_closest;
742 LOG (GNUNET_ERROR_TYPE_INFO, "Sqlite datacache running\n");
748 * Exit point from the plugin.
750 * @param cls closure (our `struct Plugin`)
754 libgnunet_plugin_datacache_sqlite_done (void *cls)
756 struct GNUNET_DATACACHE_PluginFunctions *api = cls;
757 struct Plugin *plugin = api->cls;
760 #if SQLITE_VERSION_NUMBER >= 3007000
764 #if ! WINDOWS || defined(__CYGWIN__)
765 if ((NULL != plugin->fn) && (0 != unlink (plugin->fn)))
766 LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", plugin->fn);
767 GNUNET_free_non_null (plugin->fn);
769 sqlite3_finalize (plugin->insert_stmt);
770 sqlite3_finalize (plugin->get_count_stmt);
771 sqlite3_finalize (plugin->get_stmt);
772 sqlite3_finalize (plugin->del_select_stmt);
773 sqlite3_finalize (plugin->del_expired_stmt);
774 sqlite3_finalize (plugin->del_stmt);
775 sqlite3_finalize (plugin->get_random_stmt);
776 sqlite3_finalize (plugin->get_closest_stmt);
777 result = sqlite3_close (plugin->dbh);
778 #if SQLITE_VERSION_NUMBER >= 3007000
779 if (SQLITE_BUSY == result)
781 LOG (GNUNET_ERROR_TYPE_WARNING,
783 "Tried to close sqlite without finalizing all prepared statements.\n"));
784 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
787 result = sqlite3_finalize (stmt);
788 if (result != SQLITE_OK)
789 LOG (GNUNET_ERROR_TYPE_WARNING,
790 "Failed to close statement %p: %d\n",
793 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
795 result = sqlite3_close (plugin->dbh);
798 if (SQLITE_OK != result)
799 LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
801 GNUNET_free (plugin);
807 /* end of plugin_datacache_sqlite.c */