aboutsummaryrefslogtreecommitdiff
path: root/src/namecache/plugin_namecache_postgres.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-10-16 19:32:52 +0000
committerChristian Grothoff <christian@grothoff.org>2013-10-16 19:32:52 +0000
commit6308c2556c54ea8a19b33bfe16bd2f81eae65e86 (patch)
tree2017381fa55744868e3664b59a46d60cce8c2433 /src/namecache/plugin_namecache_postgres.c
parente71d2567fc6d2634c503587ba481cc92f5f5e60e (diff)
downloadgnunet-6308c2556c54ea8a19b33bfe16bd2f81eae65e86.tar.gz
gnunet-6308c2556c54ea8a19b33bfe16bd2f81eae65e86.zip
-copied block-related functions from namestore to namecache
Diffstat (limited to 'src/namecache/plugin_namecache_postgres.c')
-rw-r--r--src/namecache/plugin_namecache_postgres.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/src/namecache/plugin_namecache_postgres.c b/src/namecache/plugin_namecache_postgres.c
new file mode 100644
index 000000000..72cfcf243
--- /dev/null
+++ b/src/namecache/plugin_namecache_postgres.c
@@ -0,0 +1,440 @@
1 /*
2 * This file is part of GNUnet
3 * (C) 2009-2013 Christian Grothoff (and other contributing authors)
4 *
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.
9 *
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.
14 *
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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @file namecache/plugin_namecache_postgres.c
23 * @brief postgres-based namecache backend
24 * @author Christian Grothoff
25 */
26#include "platform.h"
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 "namecache.h"
32
33
34/**
35 * After how many ms "busy" should a DB operation fail for good?
36 * A low value makes sure that we are more responsive to requests
37 * (especially PUTs). A high value guarantees a higher success
38 * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
39 *
40 * The default value of 1s should ensure that users do not experience
41 * huge latencies while at the same time allowing operations to succeed
42 * with reasonable probability.
43 */
44#define BUSY_TIMEOUT_MS 1000
45
46
47/**
48 * Log an error message at log-level 'level' that indicates
49 * a failure of the command 'cmd' on file 'filename'
50 * with the message given by strerror(errno).
51 */
52#define LOG_POSTGRES(db, level, cmd) do { GNUNET_log_from (level, "namecache-postgres", _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh)); } while(0)
53
54#define LOG(kind,...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__)
55
56
57/**
58 * Context for all functions in this plugin.
59 */
60struct Plugin
61{
62
63 const struct GNUNET_CONFIGURATION_Handle *cfg;
64
65 /**
66 * Native Postgres database handle.
67 */
68 PGconn *dbh;
69
70};
71
72
73/**
74 * Create our database indices.
75 *
76 * @param dbh handle to the database
77 */
78static void
79create_indices (PGconn * dbh)
80{
81 /* create indices */
82 if ( (GNUNET_OK !=
83 GNUNET_POSTGRES_exec (dbh,
84 "CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)")) ||
85 (GNUNET_OK !=
86 GNUNET_POSTGRES_exec (dbh,
87 "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)")) )
88 LOG (GNUNET_ERROR_TYPE_ERROR,
89 _("Failed to create indices\n"));
90}
91
92
93/**
94 * Initialize the database connections and associated
95 * data structures (create tables and indices
96 * as needed as well).
97 *
98 * @param plugin the plugin context (state for this module)
99 * @return GNUNET_OK on success
100 */
101static int
102database_setup (struct Plugin *plugin)
103{
104 PGresult *res;
105
106 plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg,
107 "namecache-postgres");
108 if (NULL == plugin->dbh)
109 return GNUNET_SYSERR;
110 if (GNUNET_YES ==
111 GNUNET_CONFIGURATION_get_value_yesno (plugin->cfg,
112 "namecache-postgres",
113 "TEMPORARY_TABLE"))
114 {
115 res =
116 PQexec (plugin->dbh,
117 "CREATE TEMPORARY TABLE ns096blocks ("
118 " query BYTEA NOT NULL DEFAULT '',"
119 " block BYTEA NOT NULL DEFAULT '',"
120 " expiration_time BIGINT NOT NULL DEFAULT 0"
121 ")" "WITH OIDS");
122 }
123 else
124 {
125 res =
126 PQexec (plugin->dbh,
127 "CREATE TABLE ns096blocks ("
128 " query BYTEA NOT NULL DEFAULT '',"
129 " block BYTEA NOT NULL DEFAULT '',"
130 " expiration_time BIGINT NOT NULL DEFAULT 0"
131 ")" "WITH OIDS");
132 }
133 if ( (NULL == res) ||
134 ((PQresultStatus (res) != PGRES_COMMAND_OK) &&
135 (0 != strcmp ("42P07", /* duplicate table */
136 PQresultErrorField
137 (res,
138 PG_DIAG_SQLSTATE)))))
139 {
140 (void) GNUNET_POSTGRES_check_result (plugin->dbh, res,
141 PGRES_COMMAND_OK, "CREATE TABLE",
142 "ns096blocks");
143 PQfinish (plugin->dbh);
144 plugin->dbh = NULL;
145 return GNUNET_SYSERR;
146 }
147 if (PQresultStatus (res) == PGRES_COMMAND_OK)
148 create_indices (plugin->dbh);
149 PQclear (res);
150
151 if ((GNUNET_OK !=
152 GNUNET_POSTGRES_prepare (plugin->dbh,
153 "cache_block",
154 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
155 "($1, $2, $3)", 3)) ||
156 (GNUNET_OK !=
157 GNUNET_POSTGRES_prepare (plugin->dbh,
158 "expire_blocks",
159 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1)) ||
160 (GNUNET_OK !=
161 GNUNET_POSTGRES_prepare (plugin->dbh,
162 "delete_block",
163 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2)) ||
164 (GNUNET_OK !=
165 GNUNET_POSTGRES_prepare (plugin->dbh,
166 "lookup_block",
167 "SELECT block FROM ns096blocks WHERE query=$1"
168 " ORDER BY expiration_time DESC LIMIT 1", 1)))
169 {
170 PQfinish (plugin->dbh);
171 plugin->dbh = NULL;
172 return GNUNET_SYSERR;
173 }
174 return GNUNET_OK;
175}
176
177
178/**
179 * Removes any expired block.
180 *
181 * @param plugin the plugin
182 */
183static void
184namecache_postgres_expire_blocks (struct Plugin *plugin)
185{
186 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
187 struct GNUNET_TIME_AbsoluteNBO now_be = GNUNET_TIME_absolute_hton (now);
188 const char *paramValues[] = {
189 (const char *) &now_be
190 };
191 int paramLengths[] = {
192 sizeof (now_be)
193 };
194 const int paramFormats[] = { 1 };
195 PGresult *res;
196
197 res =
198 PQexecPrepared (plugin->dbh, "expire_blocks", 1,
199 paramValues, paramLengths, paramFormats, 1);
200 if (GNUNET_OK !=
201 GNUNET_POSTGRES_check_result (plugin->dbh,
202 res,
203 PGRES_COMMAND_OK,
204 "PQexecPrepared",
205 "expire_blocks"))
206 return;
207 PQclear (res);
208}
209
210
211/**
212 * Delete older block in the datastore.
213 *
214 * @param the plugin
215 * @param query query for the block
216 * @param expiration time how old does the block have to be for deletion
217 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
218 */
219static void
220delete_old_block (struct Plugin *plugin,
221 const struct GNUNET_HashCode *query,
222 struct GNUNET_TIME_AbsoluteNBO expiration_time)
223{
224 const char *paramValues[] = {
225 (const char *) query,
226 (const char *) &expiration_time
227 };
228 int paramLengths[] = {
229 sizeof (*query),
230 sizeof (expiration_time)
231 };
232 const int paramFormats[] = { 1, 1 };
233 PGresult *res;
234
235 res =
236 PQexecPrepared (plugin->dbh, "delete_block", 2,
237 paramValues, paramLengths, paramFormats, 1);
238 if (GNUNET_OK !=
239 GNUNET_POSTGRES_check_result (plugin->dbh,
240 res,
241 PGRES_COMMAND_OK,
242 "PQexecPrepared",
243 "delete_block"))
244 return;
245 PQclear (res);
246}
247
248
249/**
250 * Cache a block in the datastore.
251 *
252 * @param cls closure (internal context for the plugin)
253 * @param block block to cache
254 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
255 */
256static int
257namecache_postgres_cache_block (void *cls,
258 const struct GNUNET_NAMESTORE_Block *block)
259{
260 struct Plugin *plugin = cls;
261 struct GNUNET_HashCode query;
262 size_t block_size = ntohl (block->purpose.size) +
263 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
264 sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
265 const char *paramValues[] = {
266 (const char *) &query,
267 (const char *) block,
268 (const char *) &block->expiration_time
269 };
270 int paramLengths[] = {
271 sizeof (query),
272 (int) block_size,
273 sizeof (block->expiration_time)
274 };
275 const int paramFormats[] = { 1, 1, 1 };
276 PGresult *res;
277
278 namecache_postgres_expire_blocks (plugin);
279 GNUNET_CRYPTO_hash (&block->derived_key,
280 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
281 &query);
282 if (block_size > 64 * 65536)
283 {
284 GNUNET_break (0);
285 return GNUNET_SYSERR;
286 }
287 delete_old_block (plugin, &query, block->expiration_time);
288
289 res =
290 PQexecPrepared (plugin->dbh, "cache_block", 3,
291 paramValues, paramLengths, paramFormats, 1);
292 if (GNUNET_OK !=
293 GNUNET_POSTGRES_check_result (plugin->dbh,
294 res,
295 PGRES_COMMAND_OK,
296 "PQexecPrepared",
297 "cache_block"))
298 return GNUNET_SYSERR;
299 PQclear (res);
300 return GNUNET_OK;
301}
302
303
304/**
305 * Get the block for a particular zone and label in the
306 * datastore. Will return at most one result to the iterator.
307 *
308 * @param cls closure (internal context for the plugin)
309 * @param query hash of public key derived from the zone and the label
310 * @param iter function to call with the result
311 * @param iter_cls closure for @a iter
312 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
313 */
314static int
315namecache_postgres_lookup_block (void *cls,
316 const struct GNUNET_HashCode *query,
317 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
318{
319 struct Plugin *plugin = cls;
320 const char *paramValues[] = {
321 (const char *) query
322 };
323 int paramLengths[] = {
324 sizeof (*query)
325 };
326 const int paramFormats[] = { 1 };
327 PGresult *res;
328 unsigned int cnt;
329 size_t bsize;
330 const struct GNUNET_NAMESTORE_Block *block;
331
332 res = PQexecPrepared (plugin->dbh,
333 "lookup_block", 1,
334 paramValues, paramLengths, paramFormats,
335 1);
336 if (GNUNET_OK !=
337 GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK,
338 "PQexecPrepared",
339 "lookup_block"))
340 {
341 LOG (GNUNET_ERROR_TYPE_DEBUG,
342 "Failing lookup (postgres error)\n");
343 return GNUNET_SYSERR;
344 }
345 if (0 == (cnt = PQntuples (res)))
346 {
347 /* no result */
348 LOG (GNUNET_ERROR_TYPE_DEBUG,
349 "Ending iteration (no more results)\n");
350 PQclear (res);
351 return GNUNET_NO;
352 }
353 GNUNET_assert (1 == cnt);
354 GNUNET_assert (1 != PQnfields (res));
355 bsize = PQgetlength (res, 0, 0);
356 block = (const struct GNUNET_NAMESTORE_Block *) PQgetvalue (res, 0, 0);
357 if ( (bsize < sizeof (*block)) ||
358 (bsize != ntohl (block->purpose.size) +
359 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
360 sizeof (struct GNUNET_CRYPTO_EcdsaSignature)) )
361 {
362 GNUNET_break (0);
363 LOG (GNUNET_ERROR_TYPE_DEBUG,
364 "Failing lookup (corrupt block)\n");
365 PQclear (res);
366 return GNUNET_SYSERR;
367 }
368 iter (iter_cls, block);
369 PQclear (res);
370 return GNUNET_OK;
371}
372
373
374/**
375 * Shutdown database connection and associate data
376 * structures.
377 *
378 * @param plugin the plugin context (state for this module)
379 */
380static void
381database_shutdown (struct Plugin *plugin)
382{
383 PQfinish (plugin->dbh);
384 plugin->dbh = NULL;
385}
386
387
388/**
389 * Entry point for the plugin.
390 *
391 * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
392 * @return NULL on error, othrewise the plugin context
393 */
394void *
395libgnunet_plugin_namecache_postgres_init (void *cls)
396{
397 static struct Plugin plugin;
398 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
399 struct GNUNET_NAMECACHE_PluginFunctions *api;
400
401 if (NULL != plugin.cfg)
402 return NULL; /* can only initialize once! */
403 memset (&plugin, 0, sizeof (struct Plugin));
404 plugin.cfg = cfg;
405 if (GNUNET_OK != database_setup (&plugin))
406 {
407 database_shutdown (&plugin);
408 return NULL;
409 }
410 api = GNUNET_new (struct GNUNET_NAMECACHE_PluginFunctions);
411 api->cls = &plugin;
412 api->cache_block = &namecache_postgres_cache_block;
413 api->lookup_block = &namecache_postgres_lookup_block;
414 LOG (GNUNET_ERROR_TYPE_INFO,
415 _("Postgres database running\n"));
416 return api;
417}
418
419
420/**
421 * Exit point from the plugin.
422 *
423 * @param cls the plugin context (as returned by "init")
424 * @return always NULL
425 */
426void *
427libgnunet_plugin_namecache_postgres_done (void *cls)
428{
429 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
430 struct Plugin *plugin = api->cls;
431
432 database_shutdown (plugin);
433 plugin->cfg = NULL;
434 GNUNET_free (api);
435 LOG (GNUNET_ERROR_TYPE_DEBUG,
436 "postgres plugin is finished\n");
437 return NULL;
438}
439
440/* end of plugin_namecache_postgres.c */