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 gn090 (hash)",
190 NULL, NULL, NULL)) ||
193 "CREATE INDEX IF NOT EXISTS idx_expire_repl ON gn090 (expire ASC,repl DESC)",
194 NULL, NULL, NULL)) ||
197 "CREATE INDEX IF NOT EXISTS idx_comb ON gn090 (anonLevel ASC,expire ASC,prio,type,hash)",
198 NULL, NULL, NULL)) ||
201 "CREATE INDEX IF NOT EXISTS idx_anon_type_hash ON gn090 (anonLevel ASC,type,hash)",
202 NULL, NULL, NULL)) ||
205 "CREATE INDEX IF NOT EXISTS idx_expire ON gn090 (expire ASC)",
206 NULL, NULL, NULL)) ||
209 "CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn090 (repl,rvalue)",
210 NULL, NULL, NULL)) ||
213 "CREATE INDEX IF NOT EXISTS idx_repl ON gn090 (repl DESC)",
215 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "sqlite",
216 "Failed to create indices: %s\n", sqlite3_errmsg (dbh));
221 #define CHECK(a) GNUNET_break(a)
225 #define ENULL_DEFINED 1
226 #define CHECK(a) if (! (a)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "%s\n", e); sqlite3_free(e); }
231 * Initialize the database connections and associated
232 * data structures (create tables and indices
233 * as needed as well).
235 * @param cfg our configuration
236 * @param plugin the plugin context (state for this module)
237 * @return #GNUNET_OK on success
240 database_setup (const struct GNUNET_CONFIGURATION_Handle *cfg,
241 struct Plugin *plugin)
250 GNUNET_CONFIGURATION_get_value_filename (cfg,
255 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
258 return GNUNET_SYSERR;
260 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
263 GNUNET_DISK_directory_create_for_file (afsdir))
266 GNUNET_free (afsdir);
267 return GNUNET_SYSERR;
269 /* database is new or got deleted, reset payload to zero! */
270 if (NULL != plugin->env->duc)
271 plugin->env->duc (plugin->env->cls,
274 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
277 /* Open database and precompile statements */
279 sqlite3_open (plugin->fn, &plugin->dbh))
281 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "sqlite",
282 _("Unable to initialize SQLite: %s.\n"),
283 sqlite3_errmsg (plugin->dbh));
284 return GNUNET_SYSERR;
287 sqlite3_exec (plugin->dbh,
288 "PRAGMA temp_store=MEMORY", NULL, NULL,
291 sqlite3_exec (plugin->dbh,
292 "PRAGMA synchronous=OFF", NULL, NULL,
295 sqlite3_exec (plugin->dbh,
296 "PRAGMA legacy_file_format=OFF", NULL, NULL,
299 sqlite3_exec (plugin->dbh,
300 "PRAGMA auto_vacuum=INCREMENTAL", NULL,
303 sqlite3_exec (plugin->dbh,
304 "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL,
307 sqlite3_exec (plugin->dbh,
308 "PRAGMA page_size=4092", NULL, NULL,
312 sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS));
315 /* We have to do it here, because otherwise precompiling SQL might fail */
317 sq_prepare (plugin->dbh,
318 "SELECT 1 FROM sqlite_master WHERE tbl_name = 'gn090'",
321 /* FIXME: SQLite does not have unsigned integers! This is ok for the type column because
322 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel
323 * we do math or inequality tests, so we can't handle the entire range of uint32_t.
324 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC.
327 sqlite3_step (stmt)) &&
329 sqlite3_exec (plugin->dbh,
330 "CREATE TABLE gn090 ("
331 " repl INT4 NOT NULL DEFAULT 0,"
332 " type INT4 NOT NULL DEFAULT 0,"
333 " prio INT4 NOT NULL DEFAULT 0,"
334 " anonLevel INT4 NOT NULL DEFAULT 0,"
335 " expire INT8 NOT NULL DEFAULT 0,"
336 " rvalue INT8 NOT NULL,"
337 " hash TEXT NOT NULL DEFAULT '',"
338 " vhash TEXT NOT NULL DEFAULT '',"
339 " value BLOB NOT NULL DEFAULT '')",
345 GNUNET_ERROR_TYPE_ERROR,
347 sqlite3_finalize (stmt);
348 return GNUNET_SYSERR;
350 sqlite3_finalize (stmt);
351 create_indices (plugin->dbh);
353 #define RESULT_COLUMNS "repl, type, prio, anonLevel, expire, hash, value, _ROWID_"
355 sq_prepare (plugin->dbh,
357 "SET prio = prio + ?, "
359 "expire = MAX(expire, ?) "
360 "WHERE hash = ? AND vhash = ?",
363 sq_prepare (plugin->dbh,
364 "UPDATE gn090 " "SET repl = MAX (0, repl - 1) WHERE _ROWID_ = ?",
365 &plugin->updRepl)) ||
367 sq_prepare (plugin->dbh,
368 "SELECT " RESULT_COLUMNS " FROM gn090 "
369 #if SQLITE_VERSION_NUMBER >= 3007000
370 "INDEXED BY idx_repl_rvalue "
372 "WHERE repl=?2 AND " " (rvalue>=?1 OR "
373 " NOT EXISTS (SELECT 1 FROM gn090 "
374 #if SQLITE_VERSION_NUMBER >= 3007000
375 "INDEXED BY idx_repl_rvalue "
377 "WHERE repl=?2 AND rvalue>=?1 LIMIT 1) ) "
378 "ORDER BY rvalue ASC LIMIT 1",
379 &plugin->selRepl)) ||
381 sq_prepare (plugin->dbh,
382 "SELECT MAX(repl) FROM gn090"
383 #if SQLITE_VERSION_NUMBER >= 3007000
384 " INDEXED BY idx_repl_rvalue"
387 &plugin->maxRepl)) ||
389 sq_prepare (plugin->dbh,
390 "SELECT " RESULT_COLUMNS " FROM gn090 "
391 #if SQLITE_VERSION_NUMBER >= 3007000
392 "INDEXED BY idx_expire "
394 "WHERE NOT EXISTS (SELECT 1 FROM gn090 WHERE expire < ?1 LIMIT 1) OR (expire < ?1) "
395 "ORDER BY expire ASC LIMIT 1",
396 &plugin->selExpi)) ||
398 sq_prepare (plugin->dbh,
399 "SELECT " RESULT_COLUMNS " FROM gn090 "
400 #if SQLITE_VERSION_NUMBER >= 3007000
401 "INDEXED BY idx_anon_type_hash "
403 "WHERE _ROWID_ >= ? AND "
406 "ORDER BY _ROWID_ ASC LIMIT 1",
407 &plugin->selZeroAnon)) ||
409 sq_prepare (plugin->dbh,
410 "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
411 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
412 &plugin->insertContent)) ||
414 sq_prepare (plugin->dbh,
415 "SELECT " RESULT_COLUMNS " FROM gn090 "
416 "WHERE _ROWID_ >= ? AND "
417 "(rvalue >= ? OR 0 = ?) AND "
418 "(hash = ? OR 0 = ?) AND "
419 "(type = ? OR 0 = ?) "
420 "ORDER BY _ROWID_ ASC LIMIT 1",
423 sq_prepare (plugin->dbh,
424 "DELETE FROM gn090 WHERE _ROWID_ = ?",
427 sq_prepare (plugin->dbh,
429 "WHERE hash = ? AND "
436 GNUNET_ERROR_TYPE_ERROR,
438 return GNUNET_SYSERR;
445 * Shutdown database connection and associate data
448 * @param plugin the plugin context (state for this module)
451 database_shutdown (struct Plugin *plugin)
454 #if SQLITE_VERSION_NUMBER >= 3007000
458 if (NULL != plugin->remove)
459 sqlite3_finalize (plugin->remove);
460 if (NULL != plugin->delRow)
461 sqlite3_finalize (plugin->delRow);
462 if (NULL != plugin->update)
463 sqlite3_finalize (plugin->update);
464 if (NULL != plugin->updRepl)
465 sqlite3_finalize (plugin->updRepl);
466 if (NULL != plugin->selRepl)
467 sqlite3_finalize (plugin->selRepl);
468 if (NULL != plugin->maxRepl)
469 sqlite3_finalize (plugin->maxRepl);
470 if (NULL != plugin->selExpi)
471 sqlite3_finalize (plugin->selExpi);
472 if (NULL != plugin->selZeroAnon)
473 sqlite3_finalize (plugin->selZeroAnon);
474 if (NULL != plugin->insertContent)
475 sqlite3_finalize (plugin->insertContent);
476 if (NULL != plugin->get)
477 sqlite3_finalize (plugin->get);
478 result = sqlite3_close (plugin->dbh);
479 #if SQLITE_VERSION_NUMBER >= 3007000
480 if (result == SQLITE_BUSY)
482 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
484 _("Tried to close sqlite without finalizing all prepared statements.\n"));
485 stmt = sqlite3_next_stmt (plugin->dbh,
489 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
491 "Closing statement %p\n",
493 result = sqlite3_finalize (stmt);
494 if (result != SQLITE_OK)
495 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
497 "Failed to close statement %p: %d\n",
500 stmt = sqlite3_next_stmt (plugin->dbh,
503 result = sqlite3_close (plugin->dbh);
506 if (SQLITE_OK != result)
508 GNUNET_ERROR_TYPE_ERROR,
510 GNUNET_free_non_null (plugin->fn);
515 * Delete the database entry with the given
518 * @param plugin the plugin context (state for this module)
519 * @param rid the ID of the row to delete
522 delete_by_rowid (struct Plugin *plugin,
525 struct GNUNET_SQ_QueryParam params[] = {
526 GNUNET_SQ_query_param_uint64 (&rid),
527 GNUNET_SQ_query_param_end
531 GNUNET_SQ_bind (plugin->delRow,
533 return GNUNET_SYSERR;
534 if (SQLITE_DONE != sqlite3_step (plugin->delRow))
536 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
538 GNUNET_SQ_reset (plugin->dbh,
540 return GNUNET_SYSERR;
542 GNUNET_SQ_reset (plugin->dbh,
549 * Store an item in the datastore.
552 * @param key key for the item
553 * @param absent true if the key was not found in the bloom filter
554 * @param size number of bytes in @a data
555 * @param data content stored
556 * @param type type of the content
557 * @param priority priority of the content
558 * @param anonymity anonymity-level for the content
559 * @param replication replication-level for the content
560 * @param expiration expiration time for the content
561 * @param cont continuation called with success or failure status
562 * @param cont_cls continuation closure
565 sqlite_plugin_put (void *cls,
566 const struct GNUNET_HashCode *key,
570 enum GNUNET_BLOCK_Type type,
573 uint32_t replication,
574 struct GNUNET_TIME_Absolute expiration,
578 struct Plugin *plugin = cls;
579 struct GNUNET_HashCode vhash;
582 GNUNET_CRYPTO_hash (data,
588 struct GNUNET_SQ_QueryParam params[] = {
589 GNUNET_SQ_query_param_uint32 (&priority),
590 GNUNET_SQ_query_param_uint32 (&replication),
591 GNUNET_SQ_query_param_absolute_time (&expiration),
592 GNUNET_SQ_query_param_auto_from_type (key),
593 GNUNET_SQ_query_param_auto_from_type (&vhash),
594 GNUNET_SQ_query_param_end
598 GNUNET_SQ_bind (plugin->update,
605 _("sqlite bind failure"));
608 if (SQLITE_DONE != sqlite3_step (plugin->update))
610 LOG_SQLITE_MSG (plugin, &msg, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
617 GNUNET_free_non_null (msg);
620 int changes = sqlite3_changes (plugin->dbh);
621 GNUNET_SQ_reset (plugin->dbh,
635 uint32_t type32 = (uint32_t) type;
636 struct GNUNET_SQ_QueryParam params[] = {
637 GNUNET_SQ_query_param_uint32 (&replication),
638 GNUNET_SQ_query_param_uint32 (&type32),
639 GNUNET_SQ_query_param_uint32 (&priority),
640 GNUNET_SQ_query_param_uint32 (&anonymity),
641 GNUNET_SQ_query_param_absolute_time (&expiration),
642 GNUNET_SQ_query_param_uint64 (&rvalue),
643 GNUNET_SQ_query_param_auto_from_type (key),
644 GNUNET_SQ_query_param_auto_from_type (&vhash),
645 GNUNET_SQ_query_param_fixed_size (data, size),
646 GNUNET_SQ_query_param_end
652 if (size > MAX_ITEM_SIZE)
654 cont (cont_cls, key, size, GNUNET_SYSERR, _("Data too large"));
657 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
658 "Storing in database block with type %u/key `%s'/priority %u/expiration in %s (%s).\n",
662 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (expiration),
664 GNUNET_STRINGS_absolute_time_to_string (expiration));
665 stmt = plugin->insertContent;
666 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
668 GNUNET_SQ_bind (stmt,
671 cont (cont_cls, key, size, GNUNET_SYSERR, NULL);
674 n = sqlite3_step (stmt);
678 if (NULL != plugin->env->duc)
679 plugin->env->duc (plugin->env->cls,
680 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
681 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
682 "Stored new entry (%u bytes)\n",
683 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
688 LOG_SQLITE_MSG (plugin, &msg, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
693 LOG_SQLITE_MSG (plugin, &msg, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
695 GNUNET_SQ_reset (plugin->dbh,
697 database_shutdown (plugin);
698 database_setup (plugin->env->cfg, plugin);
699 cont (cont_cls, key, size, GNUNET_SYSERR, msg);
700 GNUNET_free_non_null(msg);
703 GNUNET_SQ_reset (plugin->dbh,
705 cont (cont_cls, key, size, ret, msg);
706 GNUNET_free_non_null(msg);
711 * Execute statement that gets a row and call the callback
712 * with the result. Resets the statement afterwards.
714 * @param plugin the plugin
715 * @param stmt the statement
716 * @param proc processor to call
717 * @param proc_cls closure for @a proc
720 execute_get (struct Plugin *plugin,
722 PluginDatumProcessor proc,
726 struct GNUNET_TIME_Absolute expiration;
727 uint32_t replication;
734 struct GNUNET_HashCode key;
736 struct GNUNET_SQ_ResultSpec rs[] = {
737 GNUNET_SQ_result_spec_uint32 (&replication),
738 GNUNET_SQ_result_spec_uint32 (&type),
739 GNUNET_SQ_result_spec_uint32 (&priority),
740 GNUNET_SQ_result_spec_uint32 (&anonymity),
741 GNUNET_SQ_result_spec_absolute_time (&expiration),
742 GNUNET_SQ_result_spec_auto_from_type (&key),
743 GNUNET_SQ_result_spec_variable_size (&value,
745 GNUNET_SQ_result_spec_uint64 (&rowid),
746 GNUNET_SQ_result_spec_end
749 n = sqlite3_step (stmt);
754 GNUNET_SQ_extract_result (stmt,
760 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
762 "Found reply in database with expiration %s\n",
763 GNUNET_STRINGS_absolute_time_to_string (expiration));
764 ret = proc (proc_cls,
774 GNUNET_SQ_cleanup_result (rs);
775 GNUNET_SQ_reset (plugin->dbh,
777 if ( (GNUNET_NO == ret) &&
778 (GNUNET_OK == delete_by_rowid (plugin,
780 (NULL != plugin->env->duc) )
781 plugin->env->duc (plugin->env->cls,
782 -(value_size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
785 /* database must be empty */
792 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
795 sqlite3_reset (stmt))
797 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
800 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
801 database_shutdown (plugin);
802 database_setup (plugin->env->cfg,
806 GNUNET_SQ_reset (plugin->dbh,
808 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
813 * Select a subset of the items in the datastore and call
814 * the given processor for the item.
816 * @param cls our plugin context
817 * @param next_uid return the result with lowest uid >= next_uid
818 * @param type entries of which type should be considered?
819 * Must not be zero (ANY).
820 * @param proc function to call on the matching value;
821 * will be called with NULL if no value matches
822 * @param proc_cls closure for @a proc
825 sqlite_plugin_get_zero_anonymity (void *cls,
827 enum GNUNET_BLOCK_Type type,
828 PluginDatumProcessor proc,
831 struct Plugin *plugin = cls;
832 struct GNUNET_SQ_QueryParam params[] = {
833 GNUNET_SQ_query_param_uint64 (&next_uid),
834 GNUNET_SQ_query_param_uint32 (&type),
835 GNUNET_SQ_query_param_end
838 GNUNET_assert (type != GNUNET_BLOCK_TYPE_ANY);
840 GNUNET_SQ_bind (plugin->selZeroAnon,
843 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
846 execute_get (plugin, plugin->selZeroAnon, proc, proc_cls);
851 * Get results for a particular key in the datastore.
854 * @param next_uid return the result with lowest uid >= next_uid
855 * @param random if true, return a random result instead of using next_uid
856 * @param key maybe NULL (to match all entries)
857 * @param type entries of which type are relevant?
858 * Use 0 for any type.
859 * @param proc function to call on the matching value;
860 * will be called with NULL if nothing matches
861 * @param proc_cls closure for @a proc
864 sqlite_plugin_get_key (void *cls,
867 const struct GNUNET_HashCode *key,
868 enum GNUNET_BLOCK_Type type,
869 PluginDatumProcessor proc,
872 struct Plugin *plugin = cls;
874 uint16_t use_rvalue = random;
875 uint32_t type32 = (uint32_t) type;
876 uint16_t use_type = GNUNET_BLOCK_TYPE_ANY != type;
877 uint16_t use_key = NULL != key;
878 struct GNUNET_SQ_QueryParam params[] = {
879 GNUNET_SQ_query_param_uint64 (&next_uid),
880 GNUNET_SQ_query_param_uint64 (&rvalue),
881 GNUNET_SQ_query_param_uint16 (&use_rvalue),
882 GNUNET_SQ_query_param_auto_from_type (key),
883 GNUNET_SQ_query_param_uint16 (&use_key),
884 GNUNET_SQ_query_param_uint32 (&type32),
885 GNUNET_SQ_query_param_uint16 (&use_type),
886 GNUNET_SQ_query_param_end
891 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
899 GNUNET_SQ_bind (plugin->get,
902 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
913 * Context for #repl_proc() function.
919 * Function to call for the result (or the NULL).
921 PluginDatumProcessor proc;
924 * Closure for @e proc.
934 * Yes if UID was set.
941 * Wrapper for the processor for #sqlite_plugin_get_replication().
942 * Decrements the replication counter and calls the original
946 * @param key key for the content
947 * @param size number of bytes in @a data
948 * @param data content stored
949 * @param type type of the content
950 * @param priority priority of the content
951 * @param anonymity anonymity-level for the content
952 * @param replication replication-level for the content
953 * @param expiration expiration time for the content
954 * @param uid unique identifier for the datum;
955 * maybe 0 if no unique identifier is available
956 * @return #GNUNET_OK for normal return,
957 * #GNUNET_NO to delete the item
960 repl_proc (void *cls,
961 const struct GNUNET_HashCode *key,
964 enum GNUNET_BLOCK_Type type,
967 uint32_t replication,
968 struct GNUNET_TIME_Absolute expiration,
971 struct ReplCtx *rc = cls;
974 if (GNUNET_SYSERR == rc->have_uid)
975 rc->have_uid = GNUNET_NO;
976 ret = rc->proc (rc->proc_cls,
989 rc->have_uid = GNUNET_YES;
996 * Get a random item for replication. Returns a single random item
997 * from those with the highest replication counters. The item's
998 * replication counter is decremented by one IF it was positive before.
999 * Call @a proc with all values ZERO or NULL if the datastore is empty.
1001 * @param cls closure
1002 * @param proc function to call the value (once only).
1003 * @param proc_cls closure for @a proc
1006 sqlite_plugin_get_replication (void *cls,
1007 PluginDatumProcessor proc,
1010 struct Plugin *plugin = cls;
1014 struct GNUNET_SQ_QueryParam params_sel_repl[] = {
1015 GNUNET_SQ_query_param_uint64 (&rvalue),
1016 GNUNET_SQ_query_param_uint32 (&repl),
1017 GNUNET_SQ_query_param_end
1019 struct GNUNET_SQ_QueryParam params_upd_repl[] = {
1020 GNUNET_SQ_query_param_uint64 (&rc.uid),
1021 GNUNET_SQ_query_param_end
1024 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1026 "Getting random block based on replication order.\n");
1028 sqlite3_step (plugin->maxRepl))
1030 GNUNET_SQ_reset (plugin->dbh,
1033 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1036 repl = sqlite3_column_int (plugin->maxRepl,
1038 GNUNET_SQ_reset (plugin->dbh,
1040 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1043 GNUNET_SQ_bind (plugin->selRepl,
1046 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1049 rc.have_uid = GNUNET_SYSERR;
1051 rc.proc_cls = proc_cls;
1052 execute_get (plugin,
1056 if (GNUNET_YES == rc.have_uid)
1059 GNUNET_SQ_bind (plugin->updRepl,
1062 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1066 sqlite3_step (plugin->updRepl))
1068 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1070 GNUNET_SQ_reset (plugin->dbh,
1073 if (GNUNET_SYSERR == rc.have_uid)
1075 /* proc was not called at all so far, do it now. */
1076 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1082 * Get a random item that has expired or has low priority.
1083 * Call @a proc with all values ZERO or NULL if the datastore is empty.
1085 * @param cls closure
1086 * @param proc function to call the value (once only).
1087 * @param proc_cls closure for @a proc
1090 sqlite_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
1093 struct Plugin *plugin = cls;
1095 struct GNUNET_TIME_Absolute now;
1096 struct GNUNET_SQ_QueryParam params[] = {
1097 GNUNET_SQ_query_param_absolute_time (&now),
1098 GNUNET_SQ_query_param_end
1101 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1103 "Getting random block based on expiration and priority order.\n");
1104 now = GNUNET_TIME_absolute_get ();
1105 stmt = plugin->selExpi;
1107 GNUNET_SQ_bind (stmt,
1110 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1113 execute_get (plugin, stmt, proc, proc_cls);
1118 * Get all of the keys in the datastore.
1120 * @param cls closure
1121 * @param proc function to call on each key
1122 * @param proc_cls closure for @a proc
1125 sqlite_plugin_get_keys (void *cls,
1126 PluginKeyProcessor proc,
1129 struct Plugin *plugin = cls;
1130 struct GNUNET_HashCode key;
1131 struct GNUNET_SQ_ResultSpec results[] = {
1132 GNUNET_SQ_result_spec_auto_from_type (&key),
1133 GNUNET_SQ_result_spec_end
1138 GNUNET_assert (NULL != proc);
1140 sq_prepare (plugin->dbh,
1141 "SELECT hash FROM gn090",
1145 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1152 while (SQLITE_ROW == (ret = sqlite3_step (stmt)))
1155 GNUNET_SQ_extract_result (stmt,
1163 if (SQLITE_DONE != ret)
1165 GNUNET_ERROR_TYPE_ERROR,
1167 sqlite3_finalize (stmt);
1177 * @param cls our plugin context
1180 sqlite_plugin_drop (void *cls)
1182 struct Plugin *plugin = cls;
1184 plugin->drop_on_shutdown = GNUNET_YES;
1189 * Remove a particular key in the datastore.
1191 * @param cls closure
1192 * @param key key for the content
1193 * @param size number of bytes in data
1194 * @param data content stored
1195 * @param cont continuation called with success or failure status
1196 * @param cont_cls continuation closure for @a cont
1199 sqlite_plugin_remove_key (void *cls,
1200 const struct GNUNET_HashCode *key,
1203 PluginRemoveCont cont,
1206 struct Plugin *plugin = cls;
1207 struct GNUNET_SQ_QueryParam params[] = {
1208 GNUNET_SQ_query_param_auto_from_type (key),
1209 GNUNET_SQ_query_param_fixed_size (data, size),
1210 GNUNET_SQ_query_param_end
1214 GNUNET_SQ_bind (plugin->remove,
1224 if (SQLITE_DONE != sqlite3_step (plugin->remove))
1227 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1229 GNUNET_SQ_reset (plugin->dbh,
1235 "sqlite3_step failed");
1238 int changes = sqlite3_changes (plugin->dbh);
1239 GNUNET_SQ_reset (plugin->dbh,
1250 if (NULL != plugin->env->duc)
1251 plugin->env->duc (plugin->env->cls,
1252 -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
1262 * Get an estimate of how much space the database is
1265 * @param cls the `struct Plugin`
1266 * @return the size of the database on disk (estimate)
1269 sqlite_plugin_estimate_size (void *cls,
1270 unsigned long long *estimate)
1272 struct Plugin *plugin = cls;
1281 if (NULL == estimate)
1283 if (SQLITE_VERSION_NUMBER < 3006000)
1285 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1287 _("sqlite version to old to determine size, assuming zero\n"));
1292 sqlite3_exec (plugin->dbh,
1298 sqlite3_exec (plugin->dbh,
1299 "PRAGMA auto_vacuum=INCREMENTAL",
1303 sq_prepare (plugin->dbh,
1304 "PRAGMA page_count",
1306 if (SQLITE_ROW == sqlite3_step (stmt))
1307 pages = sqlite3_column_int64 (stmt,
1311 sqlite3_finalize (stmt);
1313 sq_prepare (plugin->dbh,
1316 CHECK (SQLITE_ROW ==
1317 sqlite3_step (stmt));
1318 page_size = sqlite3_column_int64 (stmt, 0);
1319 sqlite3_finalize (stmt);
1320 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1321 _("Using sqlite page utilization to estimate payload (%llu pages of size %llu bytes)\n"),
1322 (unsigned long long) pages,
1323 (unsigned long long) page_size);
1324 *estimate = pages * page_size;
1329 * Entry point for the plugin.
1331 * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment *`
1332 * @return NULL on error, othrewise the plugin context
1335 libgnunet_plugin_datastore_sqlite_init (void *cls)
1337 static struct Plugin plugin;
1338 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1339 struct GNUNET_DATASTORE_PluginFunctions *api;
1341 if (NULL != plugin.env)
1342 return NULL; /* can only initialize once! */
1345 sizeof (struct Plugin));
1347 if (GNUNET_OK != database_setup (env->cfg, &plugin))
1349 database_shutdown (&plugin);
1352 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
1354 api->estimate_size = &sqlite_plugin_estimate_size;
1355 api->put = &sqlite_plugin_put;
1356 api->get_key = &sqlite_plugin_get_key;
1357 api->get_replication = &sqlite_plugin_get_replication;
1358 api->get_expiration = &sqlite_plugin_get_expiration;
1359 api->get_zero_anonymity = &sqlite_plugin_get_zero_anonymity;
1360 api->get_keys = &sqlite_plugin_get_keys;
1361 api->drop = &sqlite_plugin_drop;
1362 api->remove_key = &sqlite_plugin_remove_key;
1363 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1365 _("Sqlite database running\n"));
1371 * Exit point from the plugin.
1373 * @param cls the plugin context (as returned by "init")
1374 * @return always NULL
1377 libgnunet_plugin_datastore_sqlite_done (void *cls)
1380 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1381 struct Plugin *plugin = api->cls;
1383 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1385 "sqlite plugin is done\n");
1387 if (plugin->drop_on_shutdown)
1388 fn = GNUNET_strdup (plugin->fn);
1389 database_shutdown (plugin);
1394 if (0 != UNLINK (fn))
1395 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1403 /* end of plugin_datastore_sqlite.c */