2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013, 2016, 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 namecache/plugin_namecache_postgres.c
23 * @brief postgres-based namecache backend
24 * @author Christian Grothoff
27 #include "gnunet_namecache_plugin.h"
28 #include "gnunet_namecache_service.h"
29 #include "gnunet_gnsrecord_lib.h"
30 #include "gnunet_postgres_lib.h"
31 #include "gnunet_pq_lib.h"
32 #include "namecache.h"
35 #define LOG(kind,...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__)
39 * Context for all functions in this plugin.
44 const struct GNUNET_CONFIGURATION_Handle *cfg;
47 * Native Postgres database handle.
55 * Initialize the database connections and associated
56 * data structures (create tables and indices
59 * @param plugin the plugin context (state for this module)
60 * @return #GNUNET_OK on success
63 database_setup (struct Plugin *plugin)
65 struct GNUNET_PQ_ExecuteStatement es_temporary =
66 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS ns096blocks ("
67 " query BYTEA NOT NULL DEFAULT '',"
68 " block BYTEA NOT NULL DEFAULT '',"
69 " expiration_time BIGINT NOT NULL DEFAULT 0"
72 struct GNUNET_PQ_ExecuteStatement es_default =
73 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
74 " query BYTEA NOT NULL DEFAULT '',"
75 " block BYTEA NOT NULL DEFAULT '',"
76 " expiration_time BIGINT NOT NULL DEFAULT 0"
79 const struct GNUNET_PQ_ExecuteStatement *cr;
81 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
82 "namecache-postgres");
83 if (NULL == plugin->dbh)
86 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
98 struct GNUNET_PQ_ExecuteStatement es[] = {
100 GNUNET_PQ_make_try_execute ("CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)"),
101 GNUNET_PQ_make_try_execute ("CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)"),
102 GNUNET_PQ_EXECUTE_STATEMENT_END
106 GNUNET_PQ_exec_statements (plugin->dbh,
109 PQfinish (plugin->dbh);
111 return GNUNET_SYSERR;
116 struct GNUNET_PQ_PreparedStatement ps[] = {
117 GNUNET_PQ_make_prepare ("cache_block",
118 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
120 GNUNET_PQ_make_prepare ("expire_blocks",
121 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1),
122 GNUNET_PQ_make_prepare ("delete_block",
123 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2),
124 GNUNET_PQ_make_prepare ("lookup_block",
125 "SELECT block FROM ns096blocks WHERE query=$1"
126 " ORDER BY expiration_time DESC LIMIT 1", 1),
127 GNUNET_PQ_PREPARED_STATEMENT_END
131 GNUNET_PQ_prepare_statements (plugin->dbh,
134 PQfinish (plugin->dbh);
136 return GNUNET_SYSERR;
145 * Removes any expired block.
147 * @param plugin the plugin
150 namecache_postgres_expire_blocks (struct Plugin *plugin)
152 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
153 struct GNUNET_PQ_QueryParam params[] = {
154 GNUNET_PQ_query_param_absolute_time (&now),
155 GNUNET_PQ_query_param_end
157 enum GNUNET_PQ_QueryStatus res;
159 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
162 GNUNET_break (GNUNET_PQ_STATUS_HARD_ERROR != res);
167 * Delete older block in the datastore.
169 * @param plugin the plugin
170 * @param query query for the block
171 * @param expiration_time how old does the block have to be for deletion
174 delete_old_block (struct Plugin *plugin,
175 const struct GNUNET_HashCode *query,
176 struct GNUNET_TIME_AbsoluteNBO expiration_time)
178 struct GNUNET_PQ_QueryParam params[] = {
179 GNUNET_PQ_query_param_auto_from_type (query),
180 GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time),
181 GNUNET_PQ_query_param_end
183 enum GNUNET_PQ_QueryStatus res;
185 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
188 GNUNET_break (GNUNET_PQ_STATUS_HARD_ERROR != res);
193 * Cache a block in the datastore.
195 * @param cls closure (internal context for the plugin)
196 * @param block block to cache
197 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
200 namecache_postgres_cache_block (void *cls,
201 const struct GNUNET_GNSRECORD_Block *block)
203 struct Plugin *plugin = cls;
204 struct GNUNET_HashCode query;
205 size_t block_size = ntohl (block->purpose.size) +
206 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
207 sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
208 struct GNUNET_PQ_QueryParam params[] = {
209 GNUNET_PQ_query_param_auto_from_type (&query),
210 GNUNET_PQ_query_param_fixed_size (block, block_size),
211 GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time),
212 GNUNET_PQ_query_param_end
214 enum GNUNET_PQ_QueryStatus res;
216 namecache_postgres_expire_blocks (plugin);
217 GNUNET_CRYPTO_hash (&block->derived_key,
218 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
220 if (block_size > 64 * 65536)
223 return GNUNET_SYSERR;
225 delete_old_block (plugin,
227 block->expiration_time);
229 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
233 return GNUNET_SYSERR;
239 * Get the block for a particular zone and label in the
240 * datastore. Will return at most one result to the iterator.
242 * @param cls closure (internal context for the plugin)
243 * @param query hash of public key derived from the zone and the label
244 * @param iter function to call with the result
245 * @param iter_cls closure for @a iter
246 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
249 namecache_postgres_lookup_block (void *cls,
250 const struct GNUNET_HashCode *query,
251 GNUNET_NAMECACHE_BlockCallback iter,
254 struct Plugin *plugin = cls;
256 struct GNUNET_GNSRECORD_Block *block;
257 struct GNUNET_PQ_QueryParam params[] = {
258 GNUNET_PQ_query_param_auto_from_type (query),
259 GNUNET_PQ_query_param_end
261 struct GNUNET_PQ_ResultSpec rs[] = {
262 GNUNET_PQ_result_spec_variable_size ("block",
265 GNUNET_PQ_result_spec_end
267 enum GNUNET_PQ_QueryStatus res;
269 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
275 LOG (GNUNET_ERROR_TYPE_WARNING,
276 "Failing lookup block in namecache (postgres error)\n");
277 return GNUNET_SYSERR;
279 if (GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS == res)
282 LOG (GNUNET_ERROR_TYPE_DEBUG,
283 "Ending iteration (no more results)\n");
286 if ( (bsize < sizeof (*block)) ||
287 (bsize != ntohl (block->purpose.size) +
288 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
289 sizeof (struct GNUNET_CRYPTO_EcdsaSignature)) )
292 LOG (GNUNET_ERROR_TYPE_DEBUG,
293 "Failing lookup (corrupt block)\n");
294 GNUNET_PQ_cleanup_result (rs);
295 return GNUNET_SYSERR;
299 GNUNET_PQ_cleanup_result (rs);
305 * Shutdown database connection and associate data
308 * @param plugin the plugin context (state for this module)
311 database_shutdown (struct Plugin *plugin)
313 PQfinish (plugin->dbh);
319 * Entry point for the plugin.
321 * @param cls the `struct GNUNET_NAMECACHE_PluginEnvironment *`
322 * @return NULL on error, otherwise the plugin context
325 libgnunet_plugin_namecache_postgres_init (void *cls)
327 static struct Plugin plugin;
328 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
329 struct GNUNET_NAMECACHE_PluginFunctions *api;
331 if (NULL != plugin.cfg)
332 return NULL; /* can only initialize once! */
333 memset (&plugin, 0, sizeof (struct Plugin));
335 if (GNUNET_OK != database_setup (&plugin))
337 database_shutdown (&plugin);
340 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
342 api->cache_block = &namecache_postgres_cache_block;
343 api->lookup_block = &namecache_postgres_lookup_block;
344 LOG (GNUNET_ERROR_TYPE_INFO,
345 "Postgres namecache plugin running\n");
351 * Exit point from the plugin.
353 * @param cls the plugin context (as returned by "init")
354 * @return always NULL
357 libgnunet_plugin_namecache_postgres_done (void *cls)
359 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
360 struct Plugin *plugin = api->cls;
362 database_shutdown (plugin);
365 LOG (GNUNET_ERROR_TYPE_DEBUG,
366 "Postgres namecache plugin is finished\n");
370 /* end of plugin_namecache_postgres.c */