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 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 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
138 sqlite3_stmt *get[8];
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)
190 sqlite3_exec (dbh, "CREATE INDEX IF NOT EXISTS idx_hash ON gn091 (hash)",
194 "CREATE INDEX IF NOT EXISTS idx_anon_type ON gn091 (anonLevel ASC,type)",
198 "CREATE INDEX IF NOT EXISTS idx_expire ON gn091 (expire ASC)",
202 "CREATE INDEX IF NOT EXISTS idx_repl_rvalue ON gn091 (repl,rvalue)",
204 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "sqlite",
205 "Failed to create indices: %s\n", sqlite3_errmsg (dbh));
210 #define CHECK(a) GNUNET_break(a)
214 #define ENULL_DEFINED 1
215 #define CHECK(a) if (! (a)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "%s\n", e); sqlite3_free(e); }
220 * Initialize the database connections and associated
221 * data structures (create tables and indices
222 * as needed as well).
224 * @param cfg our configuration
225 * @param plugin the plugin context (state for this module)
226 * @return #GNUNET_OK on success
229 database_setup (const struct GNUNET_CONFIGURATION_Handle *cfg,
230 struct Plugin *plugin)
239 GNUNET_CONFIGURATION_get_value_filename (cfg,
244 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
247 return GNUNET_SYSERR;
249 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
252 GNUNET_DISK_directory_create_for_file (afsdir))
255 GNUNET_free (afsdir);
256 return GNUNET_SYSERR;
258 /* database is new or got deleted, reset payload to zero! */
259 if (NULL != plugin->env->duc)
260 plugin->env->duc (plugin->env->cls,
263 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
266 /* Open database and precompile statements */
268 sqlite3_open (plugin->fn, &plugin->dbh))
270 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "sqlite",
271 _("Unable to initialize SQLite: %s.\n"),
272 sqlite3_errmsg (plugin->dbh));
273 return GNUNET_SYSERR;
276 sqlite3_exec (plugin->dbh,
277 "PRAGMA temp_store=MEMORY", NULL, NULL,
280 sqlite3_exec (plugin->dbh,
281 "PRAGMA synchronous=OFF", NULL, NULL,
284 sqlite3_exec (plugin->dbh,
285 "PRAGMA legacy_file_format=OFF", NULL, NULL,
288 sqlite3_exec (plugin->dbh,
289 "PRAGMA auto_vacuum=INCREMENTAL", NULL,
292 sqlite3_exec (plugin->dbh,
293 "PRAGMA locking_mode=EXCLUSIVE", NULL, NULL,
296 sqlite3_exec (plugin->dbh,
297 "PRAGMA page_size=4096", NULL, NULL,
301 sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS));
304 /* We have to do it here, because otherwise precompiling SQL might fail */
306 sq_prepare (plugin->dbh,
307 "SELECT 1 FROM sqlite_master WHERE tbl_name = 'gn091'",
310 /* FIXME: SQLite does not have unsigned integers! This is ok for the type column because
311 * we only test equality on it and can cast it to/from uint32_t. For repl, prio, and anonLevel
312 * we do math or inequality tests, so we can't handle the entire range of uint32_t.
313 * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC.
316 sqlite3_step (stmt)) &&
318 sqlite3_exec (plugin->dbh,
319 "CREATE TABLE gn091 ("
320 " repl INT4 NOT NULL DEFAULT 0,"
321 " type INT4 NOT NULL DEFAULT 0,"
322 " prio INT4 NOT NULL DEFAULT 0,"
323 " anonLevel INT4 NOT NULL DEFAULT 0,"
324 " expire INT8 NOT NULL DEFAULT 0,"
325 " rvalue INT8 NOT NULL,"
326 " hash TEXT NOT NULL DEFAULT '',"
327 " vhash TEXT NOT NULL DEFAULT '',"
328 " value BLOB NOT NULL DEFAULT '')",
334 GNUNET_ERROR_TYPE_ERROR,
336 sqlite3_finalize (stmt);
337 return GNUNET_SYSERR;
339 sqlite3_finalize (stmt);
340 create_indices (plugin->dbh);
342 #define RESULT_COLUMNS "repl, type, prio, anonLevel, expire, hash, value, _ROWID_"
344 sq_prepare (plugin->dbh,
346 "SET prio = prio + ?, "
348 "expire = MAX(expire, ?) "
349 "WHERE hash = ? AND vhash = ?",
352 sq_prepare (plugin->dbh,
353 "UPDATE gn091 " "SET repl = MAX (0, repl - 1) WHERE _ROWID_ = ?",
354 &plugin->updRepl)) ||
356 sq_prepare (plugin->dbh,
357 "SELECT " RESULT_COLUMNS " FROM gn091 "
358 "WHERE repl=?2 AND " " (rvalue>=?1 OR "
359 " NOT EXISTS (SELECT 1 FROM gn091 "
360 "WHERE repl=?2 AND rvalue>=?1 LIMIT 1) ) "
361 "ORDER BY rvalue ASC LIMIT 1",
362 &plugin->selRepl)) ||
364 sq_prepare (plugin->dbh,
365 "SELECT MAX(repl) FROM gn091",
366 &plugin->maxRepl)) ||
368 sq_prepare (plugin->dbh,
369 "SELECT " RESULT_COLUMNS " FROM gn091 "
370 "WHERE NOT EXISTS (SELECT 1 FROM gn091 WHERE expire < ?1 LIMIT 1) OR (expire < ?1) "
371 "ORDER BY expire ASC LIMIT 1",
372 &plugin->selExpi)) ||
374 sq_prepare (plugin->dbh,
375 "SELECT " RESULT_COLUMNS " FROM gn091 "
376 "WHERE _ROWID_ >= ? AND "
379 "ORDER BY _ROWID_ ASC LIMIT 1",
380 &plugin->selZeroAnon)) ||
382 sq_prepare (plugin->dbh,
383 "INSERT INTO gn091 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) "
384 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
385 &plugin->insertContent)) ||
387 sq_prepare (plugin->dbh,
388 "SELECT " RESULT_COLUMNS " FROM gn091 "
389 "WHERE _ROWID_ >= ?1 "
390 "ORDER BY _ROWID_ ASC LIMIT 1",
393 sq_prepare (plugin->dbh,
394 "SELECT " RESULT_COLUMNS " FROM gn091 "
395 "WHERE _ROWID_ >= ?1 AND "
397 "ORDER BY _ROWID_ ASC LIMIT 1",
400 sq_prepare (plugin->dbh,
401 "SELECT " RESULT_COLUMNS " FROM gn091 "
402 "WHERE _ROWID_ >= ?1 AND "
404 "ORDER BY _ROWID_ ASC LIMIT 1",
407 sq_prepare (plugin->dbh,
408 "SELECT " RESULT_COLUMNS " FROM gn091 "
409 "WHERE _ROWID_ >= ?1 AND "
412 "ORDER BY _ROWID_ ASC LIMIT 1",
415 sq_prepare (plugin->dbh,
416 "SELECT " RESULT_COLUMNS " FROM gn091 "
417 "WHERE _ROWID_ >= ?1 AND "
419 "ORDER BY _ROWID_ ASC LIMIT 1",
422 sq_prepare (plugin->dbh,
423 "SELECT " RESULT_COLUMNS " FROM gn091 "
424 "WHERE _ROWID_ >= ?1 AND "
427 "ORDER BY _ROWID_ ASC LIMIT 1",
430 sq_prepare (plugin->dbh,
431 "SELECT " RESULT_COLUMNS " FROM gn091 "
432 "WHERE _ROWID_ >= ?1 AND "
435 "ORDER BY _ROWID_ ASC LIMIT 1",
438 sq_prepare (plugin->dbh,
439 "SELECT " RESULT_COLUMNS " FROM gn091 "
440 "WHERE _ROWID_ >= ?1 AND "
444 "ORDER BY _ROWID_ ASC LIMIT 1",
447 sq_prepare (plugin->dbh,
448 "DELETE FROM gn091 WHERE _ROWID_ = ?",
451 sq_prepare (plugin->dbh,
453 "WHERE hash = ? AND "
459 GNUNET_ERROR_TYPE_ERROR,
461 return GNUNET_SYSERR;
468 * Shutdown database connection and associate data
471 * @param plugin the plugin context (state for this module)
474 database_shutdown (struct Plugin *plugin)
477 #if SQLITE_VERSION_NUMBER >= 3007000
481 if (NULL != plugin->remove)
482 sqlite3_finalize (plugin->remove);
483 if (NULL != plugin->delRow)
484 sqlite3_finalize (plugin->delRow);
485 if (NULL != plugin->update)
486 sqlite3_finalize (plugin->update);
487 if (NULL != plugin->updRepl)
488 sqlite3_finalize (plugin->updRepl);
489 if (NULL != plugin->selRepl)
490 sqlite3_finalize (plugin->selRepl);
491 if (NULL != plugin->maxRepl)
492 sqlite3_finalize (plugin->maxRepl);
493 if (NULL != plugin->selExpi)
494 sqlite3_finalize (plugin->selExpi);
495 if (NULL != plugin->selZeroAnon)
496 sqlite3_finalize (plugin->selZeroAnon);
497 if (NULL != plugin->insertContent)
498 sqlite3_finalize (plugin->insertContent);
499 for (int i = 0; i < 8; ++i)
500 if (NULL != plugin->get[i])
501 sqlite3_finalize (plugin->get[i]);
502 result = sqlite3_close (plugin->dbh);
503 #if SQLITE_VERSION_NUMBER >= 3007000
504 if (result == SQLITE_BUSY)
506 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
508 _("Tried to close sqlite without finalizing all prepared statements.\n"));
509 stmt = sqlite3_next_stmt (plugin->dbh,
513 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
515 "Closing statement %p\n",
517 result = sqlite3_finalize (stmt);
518 if (result != SQLITE_OK)
519 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
521 "Failed to close statement %p: %d\n",
524 stmt = sqlite3_next_stmt (plugin->dbh,
527 result = sqlite3_close (plugin->dbh);
530 if (SQLITE_OK != result)
532 GNUNET_ERROR_TYPE_ERROR,
534 GNUNET_free_non_null (plugin->fn);
539 * Delete the database entry with the given
542 * @param plugin the plugin context (state for this module)
543 * @param rid the ID of the row to delete
546 delete_by_rowid (struct Plugin *plugin,
549 struct GNUNET_SQ_QueryParam params[] = {
550 GNUNET_SQ_query_param_uint64 (&rid),
551 GNUNET_SQ_query_param_end
555 GNUNET_SQ_bind (plugin->delRow,
557 return GNUNET_SYSERR;
558 if (SQLITE_DONE != sqlite3_step (plugin->delRow))
560 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
562 GNUNET_SQ_reset (plugin->dbh,
564 return GNUNET_SYSERR;
566 GNUNET_SQ_reset (plugin->dbh,
573 * Store an item in the datastore.
576 * @param key key for the item
577 * @param absent true if the key was not found in the bloom filter
578 * @param size number of bytes in @a data
579 * @param data content stored
580 * @param type type of the content
581 * @param priority priority of the content
582 * @param anonymity anonymity-level for the content
583 * @param replication replication-level for the content
584 * @param expiration expiration time for the content
585 * @param cont continuation called with success or failure status
586 * @param cont_cls continuation closure
589 sqlite_plugin_put (void *cls,
590 const struct GNUNET_HashCode *key,
594 enum GNUNET_BLOCK_Type type,
597 uint32_t replication,
598 struct GNUNET_TIME_Absolute expiration,
602 struct Plugin *plugin = cls;
603 struct GNUNET_HashCode vhash;
606 GNUNET_CRYPTO_hash (data,
612 struct GNUNET_SQ_QueryParam params[] = {
613 GNUNET_SQ_query_param_uint32 (&priority),
614 GNUNET_SQ_query_param_uint32 (&replication),
615 GNUNET_SQ_query_param_absolute_time (&expiration),
616 GNUNET_SQ_query_param_auto_from_type (key),
617 GNUNET_SQ_query_param_auto_from_type (&vhash),
618 GNUNET_SQ_query_param_end
622 GNUNET_SQ_bind (plugin->update,
629 _("sqlite bind failure"));
632 if (SQLITE_DONE != sqlite3_step (plugin->update))
634 LOG_SQLITE_MSG (plugin, &msg, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
641 GNUNET_free_non_null (msg);
644 int changes = sqlite3_changes (plugin->dbh);
645 GNUNET_SQ_reset (plugin->dbh,
659 uint32_t type32 = (uint32_t) type;
660 struct GNUNET_SQ_QueryParam params[] = {
661 GNUNET_SQ_query_param_uint32 (&replication),
662 GNUNET_SQ_query_param_uint32 (&type32),
663 GNUNET_SQ_query_param_uint32 (&priority),
664 GNUNET_SQ_query_param_uint32 (&anonymity),
665 GNUNET_SQ_query_param_absolute_time (&expiration),
666 GNUNET_SQ_query_param_uint64 (&rvalue),
667 GNUNET_SQ_query_param_auto_from_type (key),
668 GNUNET_SQ_query_param_auto_from_type (&vhash),
669 GNUNET_SQ_query_param_fixed_size (data, size),
670 GNUNET_SQ_query_param_end
676 if (size > MAX_ITEM_SIZE)
678 cont (cont_cls, key, size, GNUNET_SYSERR, _("Data too large"));
681 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
682 "Storing in database block with type %u/key `%s'/priority %u/expiration in %s (%s).\n",
686 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (expiration),
688 GNUNET_STRINGS_absolute_time_to_string (expiration));
689 stmt = plugin->insertContent;
690 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
692 GNUNET_SQ_bind (stmt,
695 cont (cont_cls, key, size, GNUNET_SYSERR, NULL);
698 n = sqlite3_step (stmt);
702 if (NULL != plugin->env->duc)
703 plugin->env->duc (plugin->env->cls,
704 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
705 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
706 "Stored new entry (%u bytes)\n",
707 size + GNUNET_DATASTORE_ENTRY_OVERHEAD);
712 LOG_SQLITE_MSG (plugin,
714 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
719 LOG_SQLITE_MSG (plugin,
721 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
723 GNUNET_SQ_reset (plugin->dbh,
725 database_shutdown (plugin);
726 database_setup (plugin->env->cfg, plugin);
727 cont (cont_cls, key, size, GNUNET_SYSERR, msg);
728 GNUNET_free_non_null(msg);
731 GNUNET_SQ_reset (plugin->dbh,
733 cont (cont_cls, key, size, ret, msg);
734 GNUNET_free_non_null(msg);
739 * Execute statement that gets a row and call the callback
740 * with the result. Resets the statement afterwards.
742 * @param plugin the plugin
743 * @param stmt the statement
744 * @param proc processor to call
745 * @param proc_cls closure for @a proc
748 execute_get (struct Plugin *plugin,
750 PluginDatumProcessor proc,
754 struct GNUNET_TIME_Absolute expiration;
755 uint32_t replication;
762 struct GNUNET_HashCode key;
764 struct GNUNET_SQ_ResultSpec rs[] = {
765 GNUNET_SQ_result_spec_uint32 (&replication),
766 GNUNET_SQ_result_spec_uint32 (&type),
767 GNUNET_SQ_result_spec_uint32 (&priority),
768 GNUNET_SQ_result_spec_uint32 (&anonymity),
769 GNUNET_SQ_result_spec_absolute_time (&expiration),
770 GNUNET_SQ_result_spec_auto_from_type (&key),
771 GNUNET_SQ_result_spec_variable_size (&value,
773 GNUNET_SQ_result_spec_uint64 (&rowid),
774 GNUNET_SQ_result_spec_end
777 n = sqlite3_step (stmt);
782 GNUNET_SQ_extract_result (stmt,
788 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
790 "Found reply in database with expiration %s\n",
791 GNUNET_STRINGS_absolute_time_to_string (expiration));
792 ret = proc (proc_cls,
802 GNUNET_SQ_cleanup_result (rs);
803 GNUNET_SQ_reset (plugin->dbh,
805 if ( (GNUNET_NO == ret) &&
806 (GNUNET_OK == delete_by_rowid (plugin,
808 (NULL != plugin->env->duc) )
809 plugin->env->duc (plugin->env->cls,
810 -(value_size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
813 /* database must be empty */
820 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
823 sqlite3_reset (stmt))
825 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
828 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
829 database_shutdown (plugin);
830 database_setup (plugin->env->cfg,
834 GNUNET_SQ_reset (plugin->dbh,
836 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
841 * Select a subset of the items in the datastore and call
842 * the given processor for the item.
844 * @param cls our plugin context
845 * @param next_uid return the result with lowest uid >= next_uid
846 * @param type entries of which type should be considered?
847 * Must not be zero (ANY).
848 * @param proc function to call on the matching value;
849 * will be called with NULL if no value matches
850 * @param proc_cls closure for @a proc
853 sqlite_plugin_get_zero_anonymity (void *cls,
855 enum GNUNET_BLOCK_Type type,
856 PluginDatumProcessor proc,
859 struct Plugin *plugin = cls;
860 uint32_t type32 = type;
861 struct GNUNET_SQ_QueryParam params[] = {
862 GNUNET_SQ_query_param_uint64 (&next_uid),
863 GNUNET_SQ_query_param_uint32 (&type32),
864 GNUNET_SQ_query_param_end
867 GNUNET_assert (type != GNUNET_BLOCK_TYPE_ANY);
869 GNUNET_SQ_bind (plugin->selZeroAnon,
872 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
875 execute_get (plugin, plugin->selZeroAnon, proc, proc_cls);
880 * Get results for a particular key in the datastore.
883 * @param next_uid return the result with lowest uid >= next_uid
884 * @param random if true, return a random result instead of using next_uid
885 * @param key maybe NULL (to match all entries)
886 * @param type entries of which type are relevant?
887 * Use 0 for any type.
888 * @param proc function to call on the matching value;
889 * will be called with NULL if nothing matches
890 * @param proc_cls closure for @a proc
893 sqlite_plugin_get_key (void *cls,
896 const struct GNUNET_HashCode *key,
897 enum GNUNET_BLOCK_Type type,
898 PluginDatumProcessor proc,
901 struct Plugin *plugin = cls;
903 int use_rvalue = random;
904 uint32_t type32 = (uint32_t) type;
905 int use_type = GNUNET_BLOCK_TYPE_ANY != type;
906 int use_key = NULL != key;
907 sqlite3_stmt *stmt = plugin->get[use_rvalue * 4 + use_key * 2 + use_type];
908 struct GNUNET_SQ_QueryParam params[] = {
909 GNUNET_SQ_query_param_uint64 (&next_uid),
910 GNUNET_SQ_query_param_uint64 (&rvalue),
911 GNUNET_SQ_query_param_auto_from_type (key),
912 GNUNET_SQ_query_param_uint32 (&type32),
913 GNUNET_SQ_query_param_end
916 /* SQLite doesn't like it when you try to bind a parameter greater than the
917 * last numbered parameter, but unused parameters in the middle are OK.
921 params[3] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end;
924 params[2] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end;
926 params[1] = (struct GNUNET_SQ_QueryParam) GNUNET_SQ_query_param_end;
931 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
939 GNUNET_SQ_bind (stmt,
942 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
953 * Context for #repl_proc() function.
959 * Function to call for the result (or the NULL).
961 PluginDatumProcessor proc;
964 * Closure for @e proc.
974 * Yes if UID was set.
981 * Wrapper for the processor for #sqlite_plugin_get_replication().
982 * Decrements the replication counter and calls the original
986 * @param key key for the content
987 * @param size number of bytes in @a data
988 * @param data content stored
989 * @param type type of the content
990 * @param priority priority of the content
991 * @param anonymity anonymity-level for the content
992 * @param replication replication-level for the content
993 * @param expiration expiration time for the content
994 * @param uid unique identifier for the datum;
995 * maybe 0 if no unique identifier is available
996 * @return #GNUNET_OK for normal return,
997 * #GNUNET_NO to delete the item
1000 repl_proc (void *cls,
1001 const struct GNUNET_HashCode *key,
1004 enum GNUNET_BLOCK_Type type,
1007 uint32_t replication,
1008 struct GNUNET_TIME_Absolute expiration,
1011 struct ReplCtx *rc = cls;
1014 if (GNUNET_SYSERR == rc->have_uid)
1015 rc->have_uid = GNUNET_NO;
1016 ret = rc->proc (rc->proc_cls,
1029 rc->have_uid = GNUNET_YES;
1036 * Get a random item for replication. Returns a single random item
1037 * from those with the highest replication counters. The item's
1038 * replication counter is decremented by one IF it was positive before.
1039 * Call @a proc with all values ZERO or NULL if the datastore is empty.
1041 * @param cls closure
1042 * @param proc function to call the value (once only).
1043 * @param proc_cls closure for @a proc
1046 sqlite_plugin_get_replication (void *cls,
1047 PluginDatumProcessor proc,
1050 struct Plugin *plugin = cls;
1054 struct GNUNET_SQ_QueryParam params_sel_repl[] = {
1055 GNUNET_SQ_query_param_uint64 (&rvalue),
1056 GNUNET_SQ_query_param_uint32 (&repl),
1057 GNUNET_SQ_query_param_end
1059 struct GNUNET_SQ_QueryParam params_upd_repl[] = {
1060 GNUNET_SQ_query_param_uint64 (&rc.uid),
1061 GNUNET_SQ_query_param_end
1064 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1066 "Getting random block based on replication order.\n");
1068 sqlite3_step (plugin->maxRepl))
1070 GNUNET_SQ_reset (plugin->dbh,
1073 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1076 repl = sqlite3_column_int (plugin->maxRepl,
1078 GNUNET_SQ_reset (plugin->dbh,
1080 rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1083 GNUNET_SQ_bind (plugin->selRepl,
1086 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1089 rc.have_uid = GNUNET_SYSERR;
1091 rc.proc_cls = proc_cls;
1092 execute_get (plugin,
1096 if (GNUNET_YES == rc.have_uid)
1099 GNUNET_SQ_bind (plugin->updRepl,
1102 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1106 sqlite3_step (plugin->updRepl))
1108 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1110 GNUNET_SQ_reset (plugin->dbh,
1113 if (GNUNET_SYSERR == rc.have_uid)
1115 /* proc was not called at all so far, do it now. */
1116 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1122 * Get a random item that has expired or has low priority.
1123 * Call @a proc with all values ZERO or NULL if the datastore is empty.
1125 * @param cls closure
1126 * @param proc function to call the value (once only).
1127 * @param proc_cls closure for @a proc
1130 sqlite_plugin_get_expiration (void *cls, PluginDatumProcessor proc,
1133 struct Plugin *plugin = cls;
1135 struct GNUNET_TIME_Absolute now;
1136 struct GNUNET_SQ_QueryParam params[] = {
1137 GNUNET_SQ_query_param_absolute_time (&now),
1138 GNUNET_SQ_query_param_end
1141 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1143 "Getting random block based on expiration and priority order.\n");
1144 now = GNUNET_TIME_absolute_get ();
1145 stmt = plugin->selExpi;
1147 GNUNET_SQ_bind (stmt,
1150 proc (proc_cls, NULL, 0, NULL, 0, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0);
1153 execute_get (plugin, stmt, proc, proc_cls);
1158 * Get all of the keys in the datastore.
1160 * @param cls closure
1161 * @param proc function to call on each key
1162 * @param proc_cls closure for @a proc
1165 sqlite_plugin_get_keys (void *cls,
1166 PluginKeyProcessor proc,
1169 struct Plugin *plugin = cls;
1170 struct GNUNET_HashCode key;
1171 struct GNUNET_SQ_ResultSpec results[] = {
1172 GNUNET_SQ_result_spec_auto_from_type (&key),
1173 GNUNET_SQ_result_spec_end
1178 GNUNET_assert (NULL != proc);
1180 sq_prepare (plugin->dbh,
1181 "SELECT hash FROM gn091",
1185 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1192 while (SQLITE_ROW == (ret = sqlite3_step (stmt)))
1195 GNUNET_SQ_extract_result (stmt,
1203 if (SQLITE_DONE != ret)
1205 GNUNET_ERROR_TYPE_ERROR,
1207 sqlite3_finalize (stmt);
1217 * @param cls our plugin context
1220 sqlite_plugin_drop (void *cls)
1222 struct Plugin *plugin = cls;
1224 plugin->drop_on_shutdown = GNUNET_YES;
1229 * Remove a particular key in the datastore.
1231 * @param cls closure
1232 * @param key key for the content
1233 * @param size number of bytes in data
1234 * @param data content stored
1235 * @param cont continuation called with success or failure status
1236 * @param cont_cls continuation closure for @a cont
1239 sqlite_plugin_remove_key (void *cls,
1240 const struct GNUNET_HashCode *key,
1243 PluginRemoveCont cont,
1246 struct Plugin *plugin = cls;
1247 struct GNUNET_SQ_QueryParam params[] = {
1248 GNUNET_SQ_query_param_auto_from_type (key),
1249 GNUNET_SQ_query_param_fixed_size (data, size),
1250 GNUNET_SQ_query_param_end
1254 GNUNET_SQ_bind (plugin->remove,
1264 if (SQLITE_DONE != sqlite3_step (plugin->remove))
1267 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1269 GNUNET_SQ_reset (plugin->dbh,
1275 "sqlite3_step failed");
1278 int changes = sqlite3_changes (plugin->dbh);
1279 GNUNET_SQ_reset (plugin->dbh,
1290 if (NULL != plugin->env->duc)
1291 plugin->env->duc (plugin->env->cls,
1292 -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD));
1302 * Get an estimate of how much space the database is
1305 * @param cls the `struct Plugin`
1306 * @return the size of the database on disk (estimate)
1309 sqlite_plugin_estimate_size (void *cls,
1310 unsigned long long *estimate)
1312 struct Plugin *plugin = cls;
1321 if (NULL == estimate)
1323 if (SQLITE_VERSION_NUMBER < 3006000)
1325 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
1327 _("sqlite version to old to determine size, assuming zero\n"));
1332 sqlite3_exec (plugin->dbh,
1338 sqlite3_exec (plugin->dbh,
1339 "PRAGMA auto_vacuum=INCREMENTAL",
1343 sq_prepare (plugin->dbh,
1344 "PRAGMA page_count",
1346 if (SQLITE_ROW == sqlite3_step (stmt))
1347 pages = sqlite3_column_int64 (stmt,
1351 sqlite3_finalize (stmt);
1353 sq_prepare (plugin->dbh,
1356 CHECK (SQLITE_ROW ==
1357 sqlite3_step (stmt));
1358 page_size = sqlite3_column_int64 (stmt, 0);
1359 sqlite3_finalize (stmt);
1360 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1361 _("Using sqlite page utilization to estimate payload (%llu pages of size %llu bytes)\n"),
1362 (unsigned long long) pages,
1363 (unsigned long long) page_size);
1364 *estimate = pages * page_size;
1369 * Entry point for the plugin.
1371 * @param cls the `struct GNUNET_DATASTORE_PluginEnvironment *`
1372 * @return NULL on error, othrewise the plugin context
1375 libgnunet_plugin_datastore_sqlite_init (void *cls)
1377 static struct Plugin plugin;
1378 struct GNUNET_DATASTORE_PluginEnvironment *env = cls;
1379 struct GNUNET_DATASTORE_PluginFunctions *api;
1381 if (NULL != plugin.env)
1382 return NULL; /* can only initialize once! */
1385 sizeof (struct Plugin));
1387 if (GNUNET_OK != database_setup (env->cfg, &plugin))
1389 database_shutdown (&plugin);
1392 api = GNUNET_new (struct GNUNET_DATASTORE_PluginFunctions);
1394 api->estimate_size = &sqlite_plugin_estimate_size;
1395 api->put = &sqlite_plugin_put;
1396 api->get_key = &sqlite_plugin_get_key;
1397 api->get_replication = &sqlite_plugin_get_replication;
1398 api->get_expiration = &sqlite_plugin_get_expiration;
1399 api->get_zero_anonymity = &sqlite_plugin_get_zero_anonymity;
1400 api->get_keys = &sqlite_plugin_get_keys;
1401 api->drop = &sqlite_plugin_drop;
1402 api->remove_key = &sqlite_plugin_remove_key;
1403 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
1405 _("Sqlite database running\n"));
1411 * Exit point from the plugin.
1413 * @param cls the plugin context (as returned by "init")
1414 * @return always NULL
1417 libgnunet_plugin_datastore_sqlite_done (void *cls)
1420 struct GNUNET_DATASTORE_PluginFunctions *api = cls;
1421 struct Plugin *plugin = api->cls;
1423 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
1425 "sqlite plugin is done\n");
1427 if (plugin->drop_on_shutdown)
1428 fn = GNUNET_strdup (plugin->fn);
1429 database_shutdown (plugin);
1434 if (0 != UNLINK (fn))
1435 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1443 /* end of plugin_datastore_sqlite.c */