2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013 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/>.
20 * @file namecache/plugin_namecache_sqlite.c
21 * @brief sqlite-based namecache backend
22 * @author Christian Grothoff
25 #include "gnunet_sq_lib.h"
26 #include "gnunet_namecache_plugin.h"
27 #include "gnunet_namecache_service.h"
28 #include "gnunet_gnsrecord_lib.h"
29 #include "namecache.h"
33 * After how many ms "busy" should a DB operation fail for good? A
34 * low value makes sure that we are more responsive to requests
35 * (especially PUTs). A high value guarantees a higher success rate
36 * (SELECTs in iterate can take several seconds despite LIMIT=1).
38 * The default value of 1s should ensure that users do not experience
39 * huge latencies while at the same time allowing operations to
40 * succeed with reasonable probability.
42 #define BUSY_TIMEOUT_MS 1000
46 * Log an error message at log-level 'level' that indicates
47 * a failure of the command 'cmd' on file 'filename'
48 * with the message given by strerror(errno).
50 #define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, "namecache-sqlite", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
52 #define LOG(kind,...) GNUNET_log_from (kind, "namecache-sqlite", __VA_ARGS__)
56 * Context for all functions in this plugin.
61 const struct GNUNET_CONFIGURATION_Handle *cfg;
69 * Native SQLite database handle.
74 * Precompiled SQL for caching a block
76 sqlite3_stmt *cache_block;
79 * Precompiled SQL for deleting an older block
81 sqlite3_stmt *delete_block;
84 * Precompiled SQL for looking up a block
86 sqlite3_stmt *lookup_block;
89 * Precompiled SQL for removing expired blocks
91 sqlite3_stmt *expire_blocks;
98 * @brief Prepare a SQL statement
100 * @param dbh handle to the database
101 * @param zSql SQL statement, UTF-8 encoded
102 * @param ppStmt set to the prepared statement
103 * @return 0 on success
106 sq_prepare (sqlite3 *dbh,
108 sqlite3_stmt **ppStmt)
113 result = sqlite3_prepare_v2 (dbh,
117 (const char **) &dummy);
118 LOG (GNUNET_ERROR_TYPE_DEBUG,
119 "Prepared `%s' / %p: %d\n",
128 * Create our database indices.
130 * @param dbh handle to the database
133 create_indices (sqlite3 * dbh)
138 "CREATE INDEX IF NOT EXISTS ir_query_hash ON ns096blocks (query,expiration_time)",
139 NULL, NULL, NULL)) ||
142 "CREATE INDEX IF NOT EXISTS ir_block_expiration ON ns096blocks (expiration_time)",
144 LOG (GNUNET_ERROR_TYPE_ERROR,
145 "Failed to create indices: %s\n",
146 sqlite3_errmsg (dbh));
151 #define CHECK(a) GNUNET_break(a)
155 #define ENULL_DEFINED 1
156 #define CHECK(a) if (! (a)) { GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "%s\n", e); sqlite3_free(e); }
161 * Initialize the database connections and associated
162 * data structures (create tables and indices
163 * as needed as well).
165 * @param plugin the plugin context (state for this module)
166 * @return #GNUNET_OK on success
169 database_setup (struct Plugin *plugin)
178 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
183 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
186 return GNUNET_SYSERR;
189 GNUNET_DISK_file_test (afsdir))
192 GNUNET_DISK_directory_create_for_file (afsdir))
195 GNUNET_free (afsdir);
196 return GNUNET_SYSERR;
199 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
202 /* Open database and precompile statements */
204 sqlite3_open (plugin->fn, &plugin->dbh))
206 LOG (GNUNET_ERROR_TYPE_ERROR,
207 _("Unable to initialize SQLite: %s.\n"),
208 sqlite3_errmsg (plugin->dbh));
209 return GNUNET_SYSERR;
212 sqlite3_exec (plugin->dbh,
213 "PRAGMA temp_store=MEMORY",
217 sqlite3_exec (plugin->dbh,
218 "PRAGMA synchronous=NORMAL",
222 sqlite3_exec (plugin->dbh,
223 "PRAGMA legacy_file_format=OFF",
227 sqlite3_exec (plugin->dbh,
228 "PRAGMA auto_vacuum=INCREMENTAL",
232 sqlite3_exec (plugin->dbh,
233 "PRAGMA encoding=\"UTF-8\"",
237 sqlite3_exec (plugin->dbh,
238 "PRAGMA locking_mode=EXCLUSIVE",
242 sqlite3_exec (plugin->dbh,
243 "PRAGMA page_size=4092",
248 sqlite3_busy_timeout (plugin->dbh,
254 sq_prepare (plugin->dbh,
255 "SELECT 1 FROM sqlite_master WHERE tbl_name = 'ns096blocks'",
257 if ( (sqlite3_step (stmt) == SQLITE_DONE) &&
259 sqlite3_exec (plugin->dbh,
260 "CREATE TABLE ns096blocks ("
261 " query BLOB NOT NULL,"
262 " block BLOB NOT NULL,"
263 " expiration_time INT8 NOT NULL"
268 GNUNET_ERROR_TYPE_ERROR,
270 sqlite3_finalize (stmt);
271 return GNUNET_SYSERR;
273 sqlite3_finalize (stmt);
274 create_indices (plugin->dbh);
277 sq_prepare (plugin->dbh,
278 "INSERT INTO ns096blocks (query,block,expiration_time) VALUES (?, ?, ?)",
279 &plugin->cache_block)) ||
281 sq_prepare (plugin->dbh,
282 "DELETE FROM ns096blocks WHERE expiration_time<?",
283 &plugin->expire_blocks)) ||
285 sq_prepare (plugin->dbh,
286 "DELETE FROM ns096blocks WHERE query=? AND expiration_time<=?",
287 &plugin->delete_block)) ||
289 sq_prepare (plugin->dbh,
290 "SELECT block FROM ns096blocks WHERE query=? "
291 "ORDER BY expiration_time DESC LIMIT 1",
292 &plugin->lookup_block) )
296 GNUNET_ERROR_TYPE_ERROR,
298 return GNUNET_SYSERR;
305 * Shutdown database connection and associate data
307 * @param plugin the plugin context (state for this module)
310 database_shutdown (struct Plugin *plugin)
315 if (NULL != plugin->cache_block)
316 sqlite3_finalize (plugin->cache_block);
317 if (NULL != plugin->lookup_block)
318 sqlite3_finalize (plugin->lookup_block);
319 if (NULL != plugin->expire_blocks)
320 sqlite3_finalize (plugin->expire_blocks);
321 if (NULL != plugin->delete_block)
322 sqlite3_finalize (plugin->delete_block);
323 result = sqlite3_close (plugin->dbh);
324 if (result == SQLITE_BUSY)
326 LOG (GNUNET_ERROR_TYPE_WARNING,
327 _("Tried to close sqlite without finalizing all prepared statements.\n"));
328 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
331 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite",
332 "Closing statement %p\n", stmt);
333 result = sqlite3_finalize (stmt);
334 if (result != SQLITE_OK)
335 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "sqlite",
336 "Failed to close statement %p: %d\n", stmt, result);
337 stmt = sqlite3_next_stmt (plugin->dbh, NULL);
339 result = sqlite3_close (plugin->dbh);
341 if (SQLITE_OK != result)
342 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
344 GNUNET_free_non_null (plugin->fn);
349 * Removes any expired block.
351 * @param plugin the plugin
354 namecache_sqlite_expire_blocks (struct Plugin *plugin)
356 struct GNUNET_TIME_Absolute now;
357 struct GNUNET_SQ_QueryParam params[] = {
358 GNUNET_SQ_query_param_absolute_time (&now),
359 GNUNET_SQ_query_param_end
363 now = GNUNET_TIME_absolute_get ();
365 GNUNET_SQ_bind (plugin->expire_blocks,
368 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
369 "sqlite3_bind_XXXX");
370 GNUNET_SQ_reset (plugin->dbh,
371 plugin->expire_blocks);
374 n = sqlite3_step (plugin->expire_blocks);
375 GNUNET_SQ_reset (plugin->dbh,
376 plugin->expire_blocks);
380 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
382 "Records expired\n");
386 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
391 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
399 * Cache a block in the datastore.
401 * @param cls closure (internal context for the plugin)
402 * @param block block to cache
403 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
406 namecache_sqlite_cache_block (void *cls,
407 const struct GNUNET_GNSRECORD_Block *block)
409 struct Plugin *plugin = cls;
410 struct GNUNET_HashCode query;
411 struct GNUNET_TIME_Absolute expiration;
412 size_t block_size = ntohl (block->purpose.size) +
413 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
414 sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
415 struct GNUNET_SQ_QueryParam del_params[] = {
416 GNUNET_SQ_query_param_auto_from_type (&query),
417 GNUNET_SQ_query_param_absolute_time (&expiration),
418 GNUNET_SQ_query_param_end
420 struct GNUNET_SQ_QueryParam ins_params[] = {
421 GNUNET_SQ_query_param_auto_from_type (&query),
422 GNUNET_SQ_query_param_fixed_size (block,
424 GNUNET_SQ_query_param_absolute_time (&expiration),
425 GNUNET_SQ_query_param_end
429 namecache_sqlite_expire_blocks (plugin);
430 GNUNET_CRYPTO_hash (&block->derived_key,
431 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
433 expiration = GNUNET_TIME_absolute_ntoh (block->expiration_time);
434 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
435 "Caching new version of block %s (expires %s)\n",
437 GNUNET_STRINGS_absolute_time_to_string (expiration));
438 if (block_size > 64 * 65536)
441 return GNUNET_SYSERR;
444 /* delete old version of the block */
446 GNUNET_SQ_bind (plugin->delete_block,
449 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
450 "sqlite3_bind_XXXX");
451 GNUNET_SQ_reset (plugin->dbh,
452 plugin->delete_block);
453 return GNUNET_SYSERR;
455 n = sqlite3_step (plugin->delete_block);
459 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
461 "Old block deleted\n");
465 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
470 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
474 GNUNET_SQ_reset (plugin->dbh,
475 plugin->delete_block);
477 /* insert new version of the block */
479 GNUNET_SQ_bind (plugin->cache_block,
483 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
484 "sqlite3_bind_XXXX");
485 GNUNET_SQ_reset (plugin->dbh,
486 plugin->cache_block);
487 return GNUNET_SYSERR;
490 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491 "Caching block under derived key `%s'\n",
492 GNUNET_h2s_full (&query));
493 n = sqlite3_step (plugin->cache_block);
494 GNUNET_SQ_reset (plugin->dbh,
495 plugin->cache_block);
499 LOG (GNUNET_ERROR_TYPE_DEBUG,
504 GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
509 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
511 return GNUNET_SYSERR;
517 * Get the block for a particular zone and label in the
518 * datastore. Will return at most one result to the iterator.
520 * @param cls closure (internal context for the plugin)
521 * @param query hash of public key derived from the zone and the label
522 * @param iter function to call with the result
523 * @param iter_cls closure for @a iter
524 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
527 namecache_sqlite_lookup_block (void *cls,
528 const struct GNUNET_HashCode *query,
529 GNUNET_NAMECACHE_BlockCallback iter,
532 struct Plugin *plugin = cls;
536 const struct GNUNET_GNSRECORD_Block *block;
537 struct GNUNET_SQ_QueryParam params[] = {
538 GNUNET_SQ_query_param_auto_from_type (query),
539 GNUNET_SQ_query_param_end
541 struct GNUNET_SQ_ResultSpec rs[] = {
542 GNUNET_SQ_result_spec_variable_size ((void **) &block,
544 GNUNET_SQ_result_spec_end
548 GNUNET_SQ_bind (plugin->lookup_block,
552 GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
553 "sqlite3_bind_XXXX");
554 GNUNET_SQ_reset (plugin->dbh,
555 plugin->lookup_block);
556 return GNUNET_SYSERR;
560 (sret = sqlite3_step (plugin->lookup_block)))
563 GNUNET_SQ_extract_result (plugin->lookup_block,
569 else if ( (block_size < sizeof (struct GNUNET_GNSRECORD_Block)) ||
570 (ntohl (block->purpose.size) +
571 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
572 sizeof (struct GNUNET_CRYPTO_EcdsaSignature) != block_size) )
575 GNUNET_SQ_cleanup_result (rs);
580 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
581 "Found block under derived key `%s'\n",
582 GNUNET_h2s_full (query));
585 GNUNET_SQ_cleanup_result (rs);
591 if (SQLITE_DONE != sret)
594 GNUNET_ERROR_TYPE_ERROR,
600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601 "No block found under derived key `%s'\n",
602 GNUNET_h2s_full (query));
605 GNUNET_SQ_reset (plugin->dbh,
606 plugin->lookup_block);
612 * Entry point for the plugin.
614 * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
615 * @return NULL on error, otherwise the plugin context
618 libgnunet_plugin_namecache_sqlite_init (void *cls)
620 static struct Plugin plugin;
621 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
622 struct GNUNET_NAMECACHE_PluginFunctions *api;
624 if (NULL != plugin.cfg)
625 return NULL; /* can only initialize once! */
626 memset (&plugin, 0, sizeof (struct Plugin));
628 if (GNUNET_OK != database_setup (&plugin))
630 database_shutdown (&plugin);
633 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
635 api->cache_block = &namecache_sqlite_cache_block;
636 api->lookup_block = &namecache_sqlite_lookup_block;
637 LOG (GNUNET_ERROR_TYPE_INFO,
638 _("Sqlite database running\n"));
644 * Exit point from the plugin.
646 * @param cls the plugin context (as returned by "init")
647 * @return always NULL
650 libgnunet_plugin_namecache_sqlite_done (void *cls)
652 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
653 struct Plugin *plugin = api->cls;
655 database_shutdown (plugin);
658 LOG (GNUNET_ERROR_TYPE_DEBUG,
659 "sqlite plugin is finished\n");
663 /* end of plugin_namecache_sqlite.c */