2 * This file is part of GNUnet
3 * Copyright (C) 2009, 2011, 2017 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * @file datastore/plugin_datastore_sqlite.c
23 * @brief sqlite-based datastore backend
24 * @author Christian Grothoff
28 #include "gnunet_datastore_plugin.h"
29 #include "gnunet_sq_lib.h"
34 * We allocate items on the stack at times. To prevent a stack
35 * overflow, we impose a limit on the maximum size for the data per
36 * item. 64k should be enough.
38 #define MAX_ITEM_SIZE 65536
41 * After how many ms "busy" should a DB operation fail for good?
42 * A low value makes sure that we are more responsive to requests
43 * (especially PUTs). A high value guarantees a higher success
44 * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
46 * The default value of 250ms should ensure that users do not experience
47 * huge latencies while at the same time allowing operations to succeed
48 * with reasonable probability.
50 #define BUSY_TIMEOUT_MS 250
54 * Log an error message at log-level 'level' that indicates
55 * a failure of the command 'cmd' on file 'filename'
56 * with the message given by strerror(errno).
58 #define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
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_MSG(db, msg, level, cmd) do { GNUNET_log_from (level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); GNUNET_asprintf(msg, _("`%s' failed at %s:%u with error: %s"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
71 * Context for all functions in this plugin.
76 * Our execution environment.
78 struct GNUNET_DATASTORE_PluginEnvironment *env;
86 * Native SQLite database handle.
91 * Precompiled SQL for remove_key.
96 * Precompiled SQL for deletion.
101 * Precompiled SQL for update.
103 sqlite3_stmt *update;
106 * Get maximum repl value in database.
108 sqlite3_stmt *maxRepl;
111 * Precompiled SQL for replication decrement.
113 sqlite3_stmt *updRepl;
116 * Precompiled SQL for replication selection.
118 sqlite3_stmt *selRepl;
121 * Precompiled SQL for expiration selection.
123 sqlite3_stmt *selExpi;
126 * Precompiled SQL for expiration selection.
128 sqlite3_stmt *selZeroAnon;
131 * Precompiled SQL for insertion.
133 sqlite3_stmt *insertContent;
136 * Precompiled SQL for selection
141 * Should the database be dropped on shutdown?
143 int drop_on_shutdown;
149 * @brief Prepare a SQL statement
151 * @param dbh handle to the database
152 * @param zSql SQL statement, UTF-8 encoded
153 * @param ppStmt set to the prepared statement
154 * @return 0 on success
157 sq_prepare (sqlite3 *dbh,
159 sqlite3_stmt **ppStmt)
164 result = sqlite3_prepare_v2 (dbh,
168 (const char **) &dummy);
169 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
171 "Prepared `%s' / %p: %d\n",
180 * Create our database indices.
182 * @param dbh handle to the database
185 create_indices (sqlite3 * dbh)
189 sqlite3_exec (dbh, "CREATE INDEX IF NOT EXISTS idx_hash ON gn091 (hash)",
190 NULL, NULL, NULL)) ||
193 "CREATE INDEX IF NOT EXISTS idx_anon_type ON gn091 (anonLevel ASC,type)",
194 NULL, NULL, NULL)) ||
197 "CREATE INDEX IF NOT EXISTS idx_expire ON gn091 (expire ASC)",
198 NULL, NULL, NULL)) ||
201 "CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn091 (repl,rvalue)",
203 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "sqlite",
204 "Failed to create indices: %s\n", sqlite3_errmsg (dbh));
209 #define CHECK(a) GNUNET_break(a)
213 #define ENULL_DEFINED 1
214 #define CHECK(a) if (! (a)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "%s\n", e); sqlite3_free(e); }
219 * Initialize the database connections and associated
220 * data structures (create tables and indices
221 * as needed as well).
223 * @param cfg our configuration
224 * @param plugin the plugin context (state for this module)
225 * @return #GNUNET_OK on success
228 database_setup (const struct GNUNET_CONFIGURATION_Handle *cfg,
229 struct Plugin *plugin)
238 GNUNET_CONFIGURATION_get_value_filename (cfg,
243 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
246 return GNUNET_SYSERR;
248 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
251 GNUNET_DISK_directory_create_for_file (afsdir))
254 GNUNET_free (afsdir);
255 return GNUNET_SYSERR;
257 /* database is new or got deleted, reset payload to zero! */
258 if (NULL != plugin->env->duc)
259 plugin->env->duc (plugin->env->cls,
262 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
265 /* Open database and precompile statements */
267 sqlite3_open (plugin->fn, &plugin->dbh))
269 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "sqlite",
270 _("Unable to initialize SQLite: %s.\n"),
271 sqlite3_errmsg (plugin->dbh));
272 return GNUNET_SYSERR;
275 sqlite3_exec (plugin->dbh,
276 "PRAGMA temp_store=MEMORY", NULL, NULL,
279 sqlite3_exec (plugin->dbh,
280 "PRAGMA synchronous=OFF", NULL, NULL,
283 sqlite3_exec (plugin->dbh,
284 "PRAGMA legacy_file_format=OFF", NULL, NULL,
287 sqlite3_exec (plugin->dbh,
288 "PRAGMA auto_vacuum=INCREMENTAL", NULL,
291 sqlite3_exec (plugin->dbh,
292 "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL,
295 sqlite3_exec (plugin->dbh,
296 "PRAGMA page_size=4096", NULL, NULL,
300 sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS));
303 /* We have to do it here, because otherwise precompiling SQL might fail */
305 sq_prepare (plugin->dbh,
306 "SELECT 1 FROM sqlite_master WHERE tbl_name = 'gn091'",
309 /* FIXME: SQLite does not have unsigned integers! This is ok for the type column because
310 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel
311 * we do math or inequality tests, so we can't handle the entire range of uint32_t.
312 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC.
315 sqlite3_step (stmt)) &&
317 sqlite3_exec (plugin->dbh,
318 "CREATE TABLE gn091 ("
319 " repl INT4 NOT NULL DEFAULT 0,"
320 " type INT4 NOT NULL DEFAULT 0,"
321 " prio INT4 NOT NULL DEFAULT 0,"
322 " anonLevel INT4 NOT NULL DEFAULT 0,"
323 " expire INT8 NOT NULL DEFAULT 0,"
324 " rvalue INT8 NOT NULL,"
325 " hash TEXT NOT NULL DEFAULT '',"
326 " vhash TEXT NOT NULL DEFAULT '',"
327 " value BLOB NOT NULL DEFAULT '')",
333 GNUNET_ERROR_TYPE_ERROR,
335 sqlite3_finalize (stmt);
336 return GNUNET_SYSERR;
338 sqlite3_finalize (stmt);
339 create_indices (plugin->dbh);
341 #define RESULT_COLUMNS "repl, type, prio, anonLevel, expire, hash, value, _ROWID_"
343 sq_prepare (plugin->dbh,
345 "SET prio = prio + ?, "
347 "expire = MAX(expire, ?) "
348 "WHERE hash = ? AND vhash = ?",
351 sq_prepare (plugin->dbh,
352 "UPDATE gn091 " "SET repl = MAX (0, repl - 1) WHERE _ROWID_ = ?",
353 &plugin->updRepl)) ||
355 sq_prepare (plugin->dbh,
356 "SELECT " RESULT_COLUMNS " FROM gn091 "
357 #if SQLITE_VERSION_NUMBER >= 3007000
358 "INDEXED BY idx_repl_rvalue "
360 "WHERE repl=?2 AND " " (rvalue>=?1 OR "
361 " NOT EXISTS (SELECT 1 FROM gn091 "
362 #if SQLITE_VERSION_NUMBER >= 3007000
363 "INDEXED BY idx_repl_rvalue "
365 "WHERE repl=?2 AND rvalue>=?1 LIMIT 1) ) "
366 "ORDER BY rvalue ASC LIMIT 1",
367 &plugin->selRepl)) ||
369 sq_prepare (plugin->dbh,
370 "SELECT MAX(repl) FROM gn091"
371 #if SQLITE_VERSION_NUMBER >= 3007000
372 " INDEXED BY idx_repl_rvalue"
375 &plugin->maxRepl)) ||
377 sq_prepare (plugin->dbh,
378 "SELECT " RESULT_COLUMNS " FROM gn091 "
379 #if SQLITE_VERSION_NUMBER >= 3007000
380 "INDEXED BY idx_expire "
382 "WHERE NOT EXISTS (SELECT 1 FROM gn091 WHERE expire < ?1 LIMIT 1) OR (expire < ?1) "
383 "ORDER BY expire ASC LIMIT 1",
384 &plugin->selExpi)) ||
386 sq_prepare (plugin->dbh,
387 "SELECT " RESULT_COLUMNS " FROM gn091 "
388 #if SQLITE_VERSION_NUMBER >= 3007000
389 "INDEXED BY idx_anon_type "
391 "WHERE _ROWID_ >= ? AND "
394 "ORDER BY _ROWID_ ASC LIMIT 1",
395 &plugin->selZeroAnon)) ||
397 sq_prepare (plugin->dbh,
398 "INSERT INTO gn091 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
399 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
400 &plugin->insertContent)) ||
402 sq_prepare (plugin->dbh,
403 "SELECT " RESULT_COLUMNS " FROM gn091 "
404 "WHERE _ROWID_ >= ? AND "
405 "(rvalue >= ? OR 0 = ?) AND "
406 "(hash = ? OR 0 = ?) AND "
407 "(type = ? OR 0 = ?) "
408 "ORDER BY _ROWID_ ASC LIMIT 1",
411 sq_prepare (plugin->dbh,
412 "DELETE FROM gn091 WHERE _ROWID_ = ?",
415 sq_prepare (plugin->dbh,
417 "WHERE hash = ? AND "
423 GNUNET_ERROR_TYPE_ERROR,
425 return GNUNET_SYSERR;
432 * Shutdown database connection and associate data
435 * @param plugin the plugin context (state for this module)
438 database_shutdown (struct Plugin *plugin)
441 #if SQLITE_VERSION_NUMBER >= 3007000
445 if (NULL != plugin->remove)
446 sqlite3_finalize (plugin->remove);
447 if (NULL != plugin->delRow)
448 sqlite3_finalize (plugin->delRow);
449 if (NULL != plugin->update)
450 sqlite3_finalize (plugin->update);
451 if (NULL != plugin->updRepl)
452 sqlite3_finalize (plugin->updRepl);
453 if (NULL != plugin->selRepl)
454 sqlite3_finalize (plugin->selRepl);
455 if (NULL != plugin->maxRepl)
456 sqlite3_finalize (plugin->maxRepl);
457 if (NULL != plugin->selExpi)
458 sqlite3_finalize (plugin->selExpi);
459 if (NULL != plugin->selZeroAnon)
460 sqlite3_finalize (plugin->selZeroAnon);
461 if (NULL != plugin->insertContent)
462 sqlite3_finalize (plugin->insertContent);
463 if (NULL != plugin->get)
464 sqlite3_finalize (plugin->get);
465 result = sqlite3_close (plugin->dbh);
466 #if SQLITE_VERSION_NUMBER >= 3007000
467 if (result == SQLITE_BUSY)
469 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
471 _("Tried to close sqlite without finalizing all prepared statements.\n"));
472 stmt = sqlite3_next_stmt (plugin->dbh,
476 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
478 "Closing statement %p\n",
480 result = sqlite3_finalize (stmt);
481 if (result != SQLITE_OK)
482 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
484 "Failed to close statement %p: %d\n",
487 stmt = sqlite3_next_stmt (plugin->dbh,
490 result = sqlite3_close (plugin->dbh);
493 if (SQLITE_OK != result)
495 GNUNET_ERROR_TYPE_ERROR,
497 GNUNET_free_non_null (plugin->fn);
502 * Delete the database entry with the given
505 * @param plugin the plugin context (state for this module)
506 * @param rid the ID of the row to delete
509 delete_by_rowid (struct Plugin *plugin,
512 struct GNUNET_SQ_QueryParam params[] = {
513 GNUNET_SQ_query_param_uint64 (&rid),
514 GNUNET_SQ_query_param_end
518 GNUNET_SQ_bind (plugin->delRow,
520 return GNUNET_SYSERR;
521 if (SQLITE_DONE != sqlite3_step (plugin->delRow))
523 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
525 GNUNET_SQ_reset (plugin->dbh,
527 return GNUNET_SYSERR;
529 GNUNET_SQ_reset (plugin->dbh,
536 * Store an item in the datastore.
539 * @param key key for the item
540 * @param absent true if the key was not found in the bloom filter
541 * @param size number of bytes in @a data
542 * @param data content stored
543 * @param type type of the content
544 * @param priority priority of the content
545 * @param anonymity anonymity-level for the content
546 * @param replication replication-level for the content
547 * @param expiration expiration time for the content
548 * @param cont continuation called with success or failure status
549 * @param cont_cls continuation closure
552 sqlite_plugin_put (void *cls,
553 const struct GNUNET_HashCode *key,
557 enum GNUNET_BLOCK_Type type,
560 uint32_t replication,
561 struct GNUNET_TIME_Absolute expiration,
565 struct Plugin *plugin = cls;
566 struct GNUNET_HashCode vhash;
569 GNUNET_CRYPTO_hash (data,
575 struct GNUNET_SQ_QueryParam params[] = {
576 GNUNET_SQ_query_param_uint32 (&priority),
577 GNUNET_SQ_query_param_uint32 (&replication),
578 GNUNET_SQ_query_param_absolute_time (&expiration),
579 GNUNET_SQ_query_param_auto_from_type (key),
580 GNUNET_SQ_query_param_auto_from_type (&vhash),
581 GNUNET_SQ_query_param_end
585 GNUNET_SQ_bind (plugin->update,
592 _("sqlite bind failure"));
595 if (SQLITE_DONE != sqlite3_step (plugin->update))
597 LOG_SQLITE_MSG (plugin, &msg, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
604 GNUNET_free_non_null (msg);
607 int changes = sqlite3_changes (plugin->dbh);
608 GNUNET_SQ_reset (plugin->dbh,
622 uint32_t type32 = (uint32_t) type;
623 struct GNUNET_SQ_QueryParam params[] = {
624 GNUNET_SQ_query_param_uint32 (&replication),
625 GNUNET_SQ_query_param_uint32 (&type32),
626 GNUNET_SQ_query_param_uint32 (&priority),
627 GNUNET_SQ_query_param_uint32 (&anonymity),
628 GNUNET_SQ_query_param_absolute_time (&expiration),
629 GNUNET_SQ_query_param_uint64 (&rvalue),
630 GNUNET_SQ_query_param_auto_from_type (key),
631 GNUNET_SQ_query_param_auto_from_type (&vhash),
632 GNUNET_SQ_query_param_fixed_size (data, size),
633 GNUNET_SQ_query_param_end
639 if (size > MAX_ITEM_SIZE)
641 cont (cont_cls, key, size, GNUNET_SYSERR, _("Data too large"));
644 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
645 "Storing in database block with type %u/key `%s'/priority %u/expiration in %s (%s).\n",
649 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (expiration),
651 GNUNET_STRINGS_absolute_time_to_string (expiration));
652 stmt = plugin->insertContent;
653 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
655 GNUNET_SQ_bind (stmt,
658 cont (cont_cls, key, size, GNUNET_SYSERR, NULL);
661 n = sqlite3_step (stmt);
665 if (NULL != plugin->env->duc)
666 plugin->env->duc (plugin->env->cls,
667 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
668 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
669 "Stored new entry (%u bytes)\n",
670 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
675 LOG_SQLITE_MSG (plugin, &msg, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
680 LOG_SQLITE_MSG (plugin, &msg, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
682 GNUNET_SQ_reset (plugin->dbh,
684 database_shutdown (plugin);
685 database_setup (plugin->env->cfg, plugin);
686 cont (cont_cls, key, size, GNUNET_SYSERR, msg);
687 GNUNET_free_non_null(msg);
690 GNUNET_SQ_reset (plugin->dbh,
692 cont (cont_cls, key, size, ret, msg);
693 GNUNET_free_non_null(msg);
698 * Execute statement that gets a row and call the callback
699 * with the result. Resets the statement afterwards.
701 * @param plugin the plugin
702 * @param stmt the statement
703 * @param proc processor to call
704 * @param proc_cls closure for @a proc
707 execute_get (struct Plugin *plugin,
709 PluginDatumProcessor proc,
713 struct GNUNET_TIME_Absolute expiration;
714 uint32_t replication;
721 struct GNUNET_HashCode key;
723 struct GNUNET_SQ_ResultSpec rs[] = {
724 GNUNET_SQ_result_spec_uint32 (&replication),
725 GNUNET_SQ_result_spec_uint32 (&type),
726 GNUNET_SQ_result_spec_uint32 (&priority),
727 GNUNET_SQ_result_spec_uint32 (&anonymity),
728 GNUNET_SQ_result_spec_absolute_time (&expiration),
729 GNUNET_SQ_result_spec_auto_from_type (&key),
730 GNUNET_SQ_result_spec_variable_size (&value,
732 GNUNET_SQ_result_spec_uint64 (&rowid),
733 GNUNET_SQ_result_spec_end
736 n = sqlite3_step (stmt);
741 GNUNET_SQ_extract_result (stmt,
747 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
749 "Found reply in database with expiration %s\n",
750 GNUNET_STRINGS_absolute_time_to_string (expiration));
751 ret = proc (proc_cls,
761 GNUNET_SQ_cleanup_result (rs);
762 GNUNET_SQ_reset (plugin->dbh,
764 if ( (GNUNET_NO == ret) &&
765 (GNUNET_OK == delete_by_rowid (plugin,
767 (NULL != plugin->env->duc) )
768 plugin->env->duc (plugin->env->cls,
769 -(value_size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
772 /* database must be empty */
779 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
782 sqlite3_reset (stmt))
784 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
787 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
788 database_shutdown (plugin);
789 database_setup (plugin->env->cfg,
793 GNUNET_SQ_reset (plugin->dbh,
795 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
800 * Select a subset of the items in the datastore and call
801 * the given processor for the item.
803 * @param cls our plugin context
804 * @param next_uid return the result with lowest uid >= next_uid
805 * @param type entries of which type should be considered?
806 * Must not be zero (ANY).
807 * @param proc function to call on the matching value;
808 * will be called with NULL if no value matches
809 * @param proc_cls closure for @a proc
812 sqlite_plugin_get_zero_anonymity (void *cls,
814 enum GNUNET_BLOCK_Type type,
815 PluginDatumProcessor proc,
818 struct Plugin *plugin = cls;
819 struct GNUNET_SQ_QueryParam params[] = {
820 GNUNET_SQ_query_param_uint64 (&next_uid),
821 GNUNET_SQ_query_param_uint32 (&type),
822 GNUNET_SQ_query_param_end
825 GNUNET_assert (type != GNUNET_BLOCK_TYPE_ANY);
827 GNUNET_SQ_bind (plugin->selZeroAnon,
830 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
833 execute_get (plugin, plugin->selZeroAnon, proc, proc_cls);
838 * Get results for a particular key in the datastore.
841 * @param next_uid return the result with lowest uid >= next_uid
842 * @param random if true, return a random result instead of using next_uid
843 * @param key maybe NULL (to match all entries)
844 * @param type entries of which type are relevant?
845 * Use 0 for any type.
846 * @param proc function to call on the matching value;
847 * will be called with NULL if nothing matches
848 * @param proc_cls closure for @a proc
851 sqlite_plugin_get_key (void *cls,
854 const struct GNUNET_HashCode *key,
855 enum GNUNET_BLOCK_Type type,
856 PluginDatumProcessor proc,
859 struct Plugin *plugin = cls;
861 uint16_t use_rvalue = random;
862 uint32_t type32 = (uint32_t) type;
863 uint16_t use_type = GNUNET_BLOCK_TYPE_ANY != type;
864 uint16_t use_key = NULL != key;
865 struct GNUNET_SQ_QueryParam params[] = {
866 GNUNET_SQ_query_param_uint64 (&next_uid),
867 GNUNET_SQ_query_param_uint64 (&rvalue),
868 GNUNET_SQ_query_param_uint16 (&use_rvalue),
869 GNUNET_SQ_query_param_auto_from_type (key),
870 GNUNET_SQ_query_param_uint16 (&use_key),
871 GNUNET_SQ_query_param_uint32 (&type32),
872 GNUNET_SQ_query_param_uint16 (&use_type),
873 GNUNET_SQ_query_param_end
878 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
886 GNUNET_SQ_bind (plugin->get,
889 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
900 * Context for #repl_proc() function.
906 * Function to call for the result (or the NULL).
908 PluginDatumProcessor proc;
911 * Closure for @e proc.
921 * Yes if UID was set.
928 * Wrapper for the processor for #sqlite_plugin_get_replication().
929 * Decrements the replication counter and calls the original
933 * @param key key for the content
934 * @param size number of bytes in @a data
935 * @param data content stored
936 * @param type type of the content
937 * @param priority priority of the content
938 * @param anonymity anonymity-level for the content
939 * @param replication replication-level for the content
940 * @param expiration expiration time for the content
941 * @param uid unique identifier for the datum;
942 * maybe 0 if no unique identifier is available
943 * @return #GNUNET_OK for normal return,
944 * #GNUNET_NO to delete the item
947 repl_proc (void *cls,
948 const struct GNUNET_HashCode *key,
951 enum GNUNET_BLOCK_Type type,
954 uint32_t replication,
955 struct GNUNET_TIME_Absolute expiration,
958 struct ReplCtx *rc = cls;
961 if (GNUNET_SYSERR == rc->have_uid)
962 rc->have_uid = GNUNET_NO;
963 ret = rc->proc (rc->proc_cls,
976 rc->have_uid = GNUNET_YES;
983 * Get a random item for replication. Returns a single random item
984 * from those with the highest replication counters. The item's
985 * replication counter is decremented by one IF it was positive before.
986 * Call @a proc with all values ZERO or NULL if the datastore is empty.
989 * @param proc function to call the value (once only).
990 * @param proc_cls closure for @a proc
993 sqlite_plugin_get_replication (void *cls,
994 PluginDatumProcessor proc,
997 struct Plugin *plugin = cls;
1001 struct GNUNET_SQ_QueryParam params_sel_repl[] = {
1002 GNUNET_SQ_query_param_uint64 (&rvalue),
1003 GNUNET_SQ_query_param_uint32 (&repl),
1004 GNUNET_SQ_query_param_end
1006 struct GNUNET_SQ_QueryParam params_upd_repl[] = {
1007 GNUNET_SQ_query_param_uint64 (&rc.uid),
1008 GNUNET_SQ_query_param_end
1011 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1013 "Getting random block based on replication order.\n");
1015 sqlite3_step (plugin->maxRepl))
1017 GNUNET_SQ_reset (plugin->dbh,
1020 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1023 repl = sqlite3_column_int (plugin->maxRepl,
1025 GNUNET_SQ_reset (plugin->dbh,
1027 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1030 GNUNET_SQ_bind (plugin->selRepl,
1033 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1036 rc.have_uid = GNUNET_SYSERR;
1038 rc.proc_cls = proc_cls;
1039 execute_get (plugin,
1043 if (GNUNET_YES == rc.have_uid)
1046 GNUNET_SQ_bind (plugin->updRepl,
1049 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1053 sqlite3_step (plugin->updRepl))
1055 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1057 GNUNET_SQ_reset (plugin->dbh,
1060 if (GNUNET_SYSERR == rc.have_uid)
1062 /* proc was not called at all so far, do it now. */
1063 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1069 * Get a random item that has expired or has low priority.
1070 * Call @a proc with all values ZERO or NULL if the datastore is empty.
1072 * @param cls closure
1073 * @param proc function to call the value (once only).
1074 * @param proc_cls closure for @a proc
1077 sqlite_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
1080 struct Plugin *plugin = cls;
1082 struct GNUNET_TIME_Absolute now;
1083 struct GNUNET_SQ_QueryParam params[] = {
1084 GNUNET_SQ_query_param_absolute_time (&now),
1085 GNUNET_SQ_query_param_end
1088 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1090 "Getting random block based on expiration and priority order.\n");
1091 now = GNUNET_TIME_absolute_get ();
1092 stmt = plugin->selExpi;
1094 GNUNET_SQ_bind (stmt,
1097 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1100 execute_get (plugin, stmt, proc, proc_cls);
1105 * Get all of the keys in the datastore.
1107 * @param cls closure
1108 * @param proc function to call on each key
1109 * @param proc_cls closure for @a proc
1112 sqlite_plugin_get_keys (void *cls,
1113 PluginKeyProcessor proc,
1116 struct Plugin *plugin = cls;
1117 struct GNUNET_HashCode key;
1118 struct GNUNET_SQ_ResultSpec results[] = {
1119 GNUNET_SQ_result_spec_auto_from_type (&key),
1120 GNUNET_SQ_result_spec_end
1125 GNUNET_assert (NULL != proc);
1127 sq_prepare (plugin->dbh,
1128 "SELECT hash FROM gn091",
1132 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1139 while (SQLITE_ROW == (ret = sqlite3_step (stmt)))
1142 GNUNET_SQ_extract_result (stmt,
1150 if (SQLITE_DONE != ret)
1152 GNUNET_ERROR_TYPE_ERROR,
1154 sqlite3_finalize (stmt);
1164 * @param cls our plugin context
1167 sqlite_plugin_drop (void *cls)
1169 struct Plugin *plugin = cls;
1171 plugin->drop_on_shutdown = GNUNET_YES;
1176 * Remove a particular key in the datastore.
1178 * @param cls closure
1179 * @param key key for the content
1180 * @param size number of bytes in data
1181 * @param data content stored
1182 * @param cont continuation called with success or failure status
1183 * @param cont_cls continuation closure for @a cont
1186 sqlite_plugin_remove_key (void *cls,
1187 const struct GNUNET_HashCode *key,
1190 PluginRemoveCont cont,
1193 struct Plugin *plugin = cls;
1194 struct GNUNET_SQ_QueryParam params[] = {
1195 GNUNET_SQ_query_param_auto_from_type (key),
1196 GNUNET_SQ_query_param_fixed_size (data, size),
1197 GNUNET_SQ_query_param_end
1201 GNUNET_SQ_bind (plugin->remove,
1211 if (SQLITE_DONE != sqlite3_step (plugin->remove))
1214 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1216 GNUNET_SQ_reset (plugin->dbh,
1222 "sqlite3_step failed");
1225 int changes = sqlite3_changes (plugin->dbh);
1226 GNUNET_SQ_reset (plugin->dbh,
1237 if (NULL != plugin->env->duc)
1238 plugin->env->duc (plugin->env->cls,
1239 -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
1249 * Get an estimate of how much space the database is
1252 * @param cls the `struct Plugin`
1253 * @return the size of the database on disk (estimate)
1256 sqlite_plugin_estimate_size (void *cls,
1257 unsigned long long *estimate)
1259 struct Plugin *plugin = cls;
1268 if (NULL == estimate)
1270 if (SQLITE_VERSION_NUMBER < 3006000)
1272 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1274 _("sqlite version to old to determine size, assuming zero\n"));
1279 sqlite3_exec (plugin->dbh,
1285 sqlite3_exec (plugin->dbh,
1286 "PRAGMA auto_vacuum=INCREMENTAL",
1290 sq_prepare (plugin->dbh,
1291 "PRAGMA page_count",
1293 if (SQLITE_ROW == sqlite3_step (stmt))
1294 pages = sqlite3_column_int64 (stmt,
1298 sqlite3_finalize (stmt);
1300 sq_prepare (plugin->dbh,
1303 CHECK (SQLITE_ROW ==
1304 sqlite3_step (stmt));
1305 page_size = sqlite3_column_int64 (stmt, 0);
1306 sqlite3_finalize (stmt);
1307 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1308 _("Using sqlite page utilization to estimate payload (%llu pages of size %llu bytes)\n"),
1309 (unsigned long long) pages,
1310 (unsigned long long) page_size);
1311 *estimate = pages * page_size;
1316 * Entry point for the plugin.
1318 * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment *`
1319 * @return NULL on error, othrewise the plugin context
1322 libgnunet_plugin_datastore_sqlite_init (void *cls)
1324 static struct Plugin plugin;
1325 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1326 struct GNUNET_DATASTORE_PluginFunctions *api;
1328 if (NULL != plugin.env)
1329 return NULL; /* can only initialize once! */
1332 sizeof (struct Plugin));
1334 if (GNUNET_OK != database_setup (env->cfg, &plugin))
1336 database_shutdown (&plugin);
1339 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
1341 api->estimate_size = &sqlite_plugin_estimate_size;
1342 api->put = &sqlite_plugin_put;
1343 api->get_key = &sqlite_plugin_get_key;
1344 api->get_replication = &sqlite_plugin_get_replication;
1345 api->get_expiration = &sqlite_plugin_get_expiration;
1346 api->get_zero_anonymity = &sqlite_plugin_get_zero_anonymity;
1347 api->get_keys = &sqlite_plugin_get_keys;
1348 api->drop = &sqlite_plugin_drop;
1349 api->remove_key = &sqlite_plugin_remove_key;
1350 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1352 _("Sqlite database running\n"));
1358 * Exit point from the plugin.
1360 * @param cls the plugin context (as returned by "init")
1361 * @return always NULL
1364 libgnunet_plugin_datastore_sqlite_done (void *cls)
1367 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1368 struct Plugin *plugin = api->cls;
1370 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1372 "sqlite plugin is done\n");
1374 if (plugin->drop_on_shutdown)
1375 fn = GNUNET_strdup (plugin->fn);
1376 database_shutdown (plugin);
1381 if (0 != UNLINK (fn))
1382 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1390 /* end of plugin_datastore_sqlite.c */