aboutsummaryrefslogtreecommitdiff
path: root/src/namecache/plugin_namecache_postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/namecache/plugin_namecache_postgres.c')
-rw-r--r--src/namecache/plugin_namecache_postgres.c280
1 files changed, 113 insertions, 167 deletions
diff --git a/src/namecache/plugin_namecache_postgres.c b/src/namecache/plugin_namecache_postgres.c
index bec8bffd2..d943b0cd8 100644
--- a/src/namecache/plugin_namecache_postgres.c
+++ b/src/namecache/plugin_namecache_postgres.c
@@ -1,6 +1,6 @@
1 /* 1 /*
2 * This file is part of GNUnet 2 * This file is part of GNUnet
3 * Copyright (C) 2009-2013, 2016 GNUnet e.V. 3 * Copyright (C) 2009-2013, 2016, 2017 GNUnet e.V.
4 * 4 *
5 * GNUnet is free software; you can redistribute it and/or modify 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 6 * it under the terms of the GNU General Public License as published
@@ -27,31 +27,10 @@
27#include "gnunet_namecache_plugin.h" 27#include "gnunet_namecache_plugin.h"
28#include "gnunet_namecache_service.h" 28#include "gnunet_namecache_service.h"
29#include "gnunet_gnsrecord_lib.h" 29#include "gnunet_gnsrecord_lib.h"
30#include "gnunet_postgres_lib.h"
31#include "gnunet_pq_lib.h" 30#include "gnunet_pq_lib.h"
32#include "namecache.h" 31#include "namecache.h"
33 32
34 33
35/**
36 * After how many ms "busy" should a DB operation fail for good?
37 * A low value makes sure that we are more responsive to requests
38 * (especially PUTs). A high value guarantees a higher success
39 * rate (SELECTs in iterate can take several seconds despite LIMIT=1).
40 *
41 * The default value of 1s should ensure that users do not experience
42 * huge latencies while at the same time allowing operations to succeed
43 * with reasonable probability.
44 */
45#define BUSY_TIMEOUT_MS 1000
46
47
48/**
49 * Log an error message at log-level 'level' that indicates
50 * a failure of the command 'cmd' on file 'filename'
51 * with the message given by strerror(errno).
52 */
53#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)
54
55#define LOG(kind,...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__) 34#define LOG(kind,...) GNUNET_log_from (kind, "namecache-postgres", __VA_ARGS__)
56 35
57 36
@@ -72,40 +51,34 @@ struct Plugin
72 51
73 52
74/** 53/**
75 * Create our database indices.
76 *
77 * @param dbh handle to the database
78 */
79static void
80create_indices (PGconn * dbh)
81{
82 /* create indices */
83 if ( (GNUNET_OK !=
84 GNUNET_POSTGRES_exec (dbh,
85 "CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)")) ||
86 (GNUNET_OK !=
87 GNUNET_POSTGRES_exec (dbh,
88 "CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)")) )
89 LOG (GNUNET_ERROR_TYPE_ERROR,
90 _("Failed to create indices\n"));
91}
92
93
94/**
95 * Initialize the database connections and associated 54 * Initialize the database connections and associated
96 * data structures (create tables and indices 55 * data structures (create tables and indices
97 * as needed as well). 56 * as needed as well).
98 * 57 *
99 * @param plugin the plugin context (state for this module) 58 * @param plugin the plugin context (state for this module)
100 * @return GNUNET_OK on success 59 * @return #GNUNET_OK on success
101 */ 60 */
102static int 61static int
103database_setup (struct Plugin *plugin) 62database_setup (struct Plugin *plugin)
104{ 63{
105 PGresult *res; 64 struct GNUNET_PQ_ExecuteStatement es_temporary =
106 65 GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS ns096blocks ("
107 plugin->dbh = GNUNET_POSTGRES_connect (plugin->cfg, 66 " query BYTEA NOT NULL DEFAULT '',"
108 "namecache-postgres"); 67 " block BYTEA NOT NULL DEFAULT '',"
68 " expiration_time BIGINT NOT NULL DEFAULT 0"
69 ")"
70 "WITH OIDS");
71 struct GNUNET_PQ_ExecuteStatement es_default =
72 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS ns096blocks ("
73 " query BYTEA NOT NULL DEFAULT '',"
74 " block BYTEA NOT NULL DEFAULT '',"
75 " expiration_time BIGINT NOT NULL DEFAULT 0"
76 ")"
77 "WITH OIDS");
78 const struct GNUNET_PQ_ExecuteStatement *cr;
79
80 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
81 "namecache-postgres");
109 if (NULL == plugin->dbh) 82 if (NULL == plugin->dbh)
110 return GNUNET_SYSERR; 83 return GNUNET_SYSERR;
111 if (GNUNET_YES == 84 if (GNUNET_YES ==
@@ -113,65 +86,56 @@ database_setup (struct Plugin *plugin)
113 "namecache-postgres", 86 "namecache-postgres",
114 "TEMPORARY_TABLE")) 87 "TEMPORARY_TABLE"))
115 { 88 {
116 res = 89 cr = &es_temporary;
117 PQexec (plugin->dbh,
118 "CREATE TEMPORARY TABLE ns096blocks ("
119 " query BYTEA NOT NULL DEFAULT '',"
120 " block BYTEA NOT NULL DEFAULT '',"
121 " expiration_time BIGINT NOT NULL DEFAULT 0"
122 ")" "WITH OIDS");
123 } 90 }
124 else 91 else
125 { 92 {
126 res = 93 cr = &es_default;
127 PQexec (plugin->dbh,
128 "CREATE TABLE ns096blocks ("
129 " query BYTEA NOT NULL DEFAULT '',"
130 " block BYTEA NOT NULL DEFAULT '',"
131 " expiration_time BIGINT NOT NULL DEFAULT 0"
132 ")" "WITH OIDS");
133 } 94 }
134 if ( (NULL == res) || 95
135 ((PQresultStatus (res) != PGRES_COMMAND_OK) &&
136 (0 != strcmp ("42P07", /* duplicate table */
137 PQresultErrorField
138 (res,
139 PG_DIAG_SQLSTATE)))))
140 { 96 {
141 (void) GNUNET_POSTGRES_check_result (plugin->dbh, res, 97 struct GNUNET_PQ_ExecuteStatement es[] = {
142 PGRES_COMMAND_OK, "CREATE TABLE", 98 *cr,
143 "ns096blocks"); 99 GNUNET_PQ_make_try_execute ("CREATE INDEX ir_query_hash ON ns096blocks (query,expiration_time)"),
144 PQfinish (plugin->dbh); 100 GNUNET_PQ_make_try_execute ("CREATE INDEX ir_block_expiration ON ns096blocks (expiration_time)"),
145 plugin->dbh = NULL; 101 GNUNET_PQ_EXECUTE_STATEMENT_END
146 return GNUNET_SYSERR; 102 };
103
104 if (GNUNET_OK !=
105 GNUNET_PQ_exec_statements (plugin->dbh,
106 es))
107 {
108 PQfinish (plugin->dbh);
109 plugin->dbh = NULL;
110 return GNUNET_SYSERR;
111 }
147 } 112 }
148 if (PQresultStatus (res) == PGRES_COMMAND_OK) 113
149 create_indices (plugin->dbh);
150 PQclear (res);
151
152 if ((GNUNET_OK !=
153 GNUNET_POSTGRES_prepare (plugin->dbh,
154 "cache_block",
155 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
156 "($1, $2, $3)", 3)) ||
157 (GNUNET_OK !=
158 GNUNET_POSTGRES_prepare (plugin->dbh,
159 "expire_blocks",
160 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1)) ||
161 (GNUNET_OK !=
162 GNUNET_POSTGRES_prepare (plugin->dbh,
163 "delete_block",
164 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2)) ||
165 (GNUNET_OK !=
166 GNUNET_POSTGRES_prepare (plugin->dbh,
167 "lookup_block",
168 "SELECT block FROM ns096blocks WHERE query=$1"
169 " ORDER BY expiration_time DESC LIMIT 1", 1)))
170 { 114 {
171 PQfinish (plugin->dbh); 115 struct GNUNET_PQ_PreparedStatement ps[] = {
172 plugin->dbh = NULL; 116 GNUNET_PQ_make_prepare ("cache_block",
173 return GNUNET_SYSERR; 117 "INSERT INTO ns096blocks (query, block, expiration_time) VALUES "
118 "($1, $2, $3)", 3),
119 GNUNET_PQ_make_prepare ("expire_blocks",
120 "DELETE FROM ns096blocks WHERE expiration_time<$1", 1),
121 GNUNET_PQ_make_prepare ("delete_block",
122 "DELETE FROM ns096blocks WHERE query=$1 AND expiration_time<=$2", 2),
123 GNUNET_PQ_make_prepare ("lookup_block",
124 "SELECT block FROM ns096blocks WHERE query=$1"
125 " ORDER BY expiration_time DESC LIMIT 1", 1),
126 GNUNET_PQ_PREPARED_STATEMENT_END
127 };
128
129 if (GNUNET_OK !=
130 GNUNET_PQ_prepare_statements (plugin->dbh,
131 ps))
132 {
133 PQfinish (plugin->dbh);
134 plugin->dbh = NULL;
135 return GNUNET_SYSERR;
136 }
174 } 137 }
138
175 return GNUNET_OK; 139 return GNUNET_OK;
176} 140}
177 141
@@ -185,23 +149,16 @@ static void
185namecache_postgres_expire_blocks (struct Plugin *plugin) 149namecache_postgres_expire_blocks (struct Plugin *plugin)
186{ 150{
187 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); 151 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
188 struct GNUNET_PQ_QueryParam params[] = { 152 struct GNUNET_PQ_QueryParam params[] = {
189 GNUNET_PQ_query_param_absolute_time (&now), 153 GNUNET_PQ_query_param_absolute_time (&now),
190 GNUNET_PQ_query_param_end 154 GNUNET_PQ_query_param_end
191 }; 155 };
192 PGresult *res; 156 enum GNUNET_DB_QueryStatus res;
193 157
194 res = GNUNET_PQ_exec_prepared (plugin->dbh, 158 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
195 "expire_blocks", 159 "expire_blocks",
196 params); 160 params);
197 if (GNUNET_OK != 161 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res);
198 GNUNET_POSTGRES_check_result (plugin->dbh,
199 res,
200 PGRES_COMMAND_OK,
201 "PQexecPrepared",
202 "expire_blocks"))
203 return;
204 PQclear (res);
205} 162}
206 163
207 164
@@ -217,24 +174,17 @@ delete_old_block (struct Plugin *plugin,
217 const struct GNUNET_HashCode *query, 174 const struct GNUNET_HashCode *query,
218 struct GNUNET_TIME_AbsoluteNBO expiration_time) 175 struct GNUNET_TIME_AbsoluteNBO expiration_time)
219{ 176{
220 struct GNUNET_PQ_QueryParam params[] = { 177 struct GNUNET_PQ_QueryParam params[] = {
221 GNUNET_PQ_query_param_auto_from_type (query), 178 GNUNET_PQ_query_param_auto_from_type (query),
222 GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time), 179 GNUNET_PQ_query_param_absolute_time_nbo (&expiration_time),
223 GNUNET_PQ_query_param_end 180 GNUNET_PQ_query_param_end
224 }; 181 };
225 PGresult *res; 182 enum GNUNET_DB_QueryStatus res;
226 183
227 res = GNUNET_PQ_exec_prepared (plugin->dbh, 184 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
228 "delete_block", 185 "delete_block",
229 params); 186 params);
230 if (GNUNET_OK != 187 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != res);
231 GNUNET_POSTGRES_check_result (plugin->dbh,
232 res,
233 PGRES_COMMAND_OK,
234 "PQexecPrepared",
235 "delete_block"))
236 return;
237 PQclear (res);
238} 188}
239 189
240 190
@@ -254,13 +204,13 @@ namecache_postgres_cache_block (void *cls,
254 size_t block_size = ntohl (block->purpose.size) + 204 size_t block_size = ntohl (block->purpose.size) +
255 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) + 205 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
256 sizeof (struct GNUNET_CRYPTO_EcdsaSignature); 206 sizeof (struct GNUNET_CRYPTO_EcdsaSignature);
257 struct GNUNET_PQ_QueryParam params[] = { 207 struct GNUNET_PQ_QueryParam params[] = {
258 GNUNET_PQ_query_param_auto_from_type (&query), 208 GNUNET_PQ_query_param_auto_from_type (&query),
259 GNUNET_PQ_query_param_fixed_size (block, block_size), 209 GNUNET_PQ_query_param_fixed_size (block, block_size),
260 GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time), 210 GNUNET_PQ_query_param_absolute_time_nbo (&block->expiration_time),
261 GNUNET_PQ_query_param_end 211 GNUNET_PQ_query_param_end
262 }; 212 };
263 PGresult *res; 213 enum GNUNET_DB_QueryStatus res;
264 214
265 namecache_postgres_expire_blocks (plugin); 215 namecache_postgres_expire_blocks (plugin);
266 GNUNET_CRYPTO_hash (&block->derived_key, 216 GNUNET_CRYPTO_hash (&block->derived_key,
@@ -271,19 +221,15 @@ namecache_postgres_cache_block (void *cls,
271 GNUNET_break (0); 221 GNUNET_break (0);
272 return GNUNET_SYSERR; 222 return GNUNET_SYSERR;
273 } 223 }
274 delete_old_block (plugin, &query, block->expiration_time); 224 delete_old_block (plugin,
275 225 &query,
276 res = GNUNET_PQ_exec_prepared (plugin->dbh, 226 block->expiration_time);
277 "cache_block", 227
278 params); 228 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
279 if (GNUNET_OK != 229 "cache_block",
280 GNUNET_POSTGRES_check_result (plugin->dbh, 230 params);
281 res, 231 if (0 > res)
282 PGRES_COMMAND_OK,
283 "PQexecPrepared",
284 "cache_block"))
285 return GNUNET_SYSERR; 232 return GNUNET_SYSERR;
286 PQclear (res);
287 return GNUNET_OK; 233 return GNUNET_OK;
288} 234}
289 235
@@ -301,42 +247,41 @@ namecache_postgres_cache_block (void *cls,
301static int 247static int
302namecache_postgres_lookup_block (void *cls, 248namecache_postgres_lookup_block (void *cls,
303 const struct GNUNET_HashCode *query, 249 const struct GNUNET_HashCode *query,
304 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls) 250 GNUNET_NAMECACHE_BlockCallback iter,
251 void *iter_cls)
305{ 252{
306 struct Plugin *plugin = cls; 253 struct Plugin *plugin = cls;
307 struct GNUNET_PQ_QueryParam params[] = { 254 size_t bsize;
255 struct GNUNET_GNSRECORD_Block *block;
256 struct GNUNET_PQ_QueryParam params[] = {
308 GNUNET_PQ_query_param_auto_from_type (query), 257 GNUNET_PQ_query_param_auto_from_type (query),
309 GNUNET_PQ_query_param_end 258 GNUNET_PQ_query_param_end
310 }; 259 };
311 PGresult *res; 260 struct GNUNET_PQ_ResultSpec rs[] = {
312 unsigned int cnt; 261 GNUNET_PQ_result_spec_variable_size ("block",
313 size_t bsize; 262 (void **) &block,
314 const struct GNUNET_GNSRECORD_Block *block; 263 &bsize),
315 264 GNUNET_PQ_result_spec_end
316 res = GNUNET_PQ_exec_prepared (plugin->dbh, 265 };
317 "lookup_block", 266 enum GNUNET_DB_QueryStatus res;
318 params); 267
319 if (GNUNET_OK != 268 res = GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh,
320 GNUNET_POSTGRES_check_result (plugin->dbh, res, PGRES_TUPLES_OK, 269 "lookup_block",
321 "PQexecPrepared", 270 params,
322 "lookup_block")) 271 rs);
272 if (0 > res)
323 { 273 {
324 LOG (GNUNET_ERROR_TYPE_DEBUG, 274 LOG (GNUNET_ERROR_TYPE_WARNING,
325 "Failing lookup (postgres error)\n"); 275 "Failing lookup block in namecache (postgres error)\n");
326 return GNUNET_SYSERR; 276 return GNUNET_SYSERR;
327 } 277 }
328 if (0 == (cnt = PQntuples (res))) 278 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == res)
329 { 279 {
330 /* no result */ 280 /* no result */
331 LOG (GNUNET_ERROR_TYPE_DEBUG, 281 LOG (GNUNET_ERROR_TYPE_DEBUG,
332 "Ending iteration (no more results)\n"); 282 "Ending iteration (no more results)\n");
333 PQclear (res);
334 return GNUNET_NO; 283 return GNUNET_NO;
335 } 284 }
336 GNUNET_assert (1 == cnt);
337 GNUNET_assert (1 != PQnfields (res));
338 bsize = PQgetlength (res, 0, 0);
339 block = (const struct GNUNET_GNSRECORD_Block *) PQgetvalue (res, 0, 0);
340 if ( (bsize < sizeof (*block)) || 285 if ( (bsize < sizeof (*block)) ||
341 (bsize != ntohl (block->purpose.size) + 286 (bsize != ntohl (block->purpose.size) +
342 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) + 287 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
@@ -345,11 +290,12 @@ namecache_postgres_lookup_block (void *cls,
345 GNUNET_break (0); 290 GNUNET_break (0);
346 LOG (GNUNET_ERROR_TYPE_DEBUG, 291 LOG (GNUNET_ERROR_TYPE_DEBUG,
347 "Failing lookup (corrupt block)\n"); 292 "Failing lookup (corrupt block)\n");
348 PQclear (res); 293 GNUNET_PQ_cleanup_result (rs);
349 return GNUNET_SYSERR; 294 return GNUNET_SYSERR;
350 } 295 }
351 iter (iter_cls, block); 296 iter (iter_cls,
352 PQclear (res); 297 block);
298 GNUNET_PQ_cleanup_result (rs);
353 return GNUNET_OK; 299 return GNUNET_OK;
354} 300}
355 301
@@ -395,7 +341,7 @@ libgnunet_plugin_namecache_postgres_init (void *cls)
395 api->cache_block = &namecache_postgres_cache_block; 341 api->cache_block = &namecache_postgres_cache_block;
396 api->lookup_block = &namecache_postgres_lookup_block; 342 api->lookup_block = &namecache_postgres_lookup_block;
397 LOG (GNUNET_ERROR_TYPE_INFO, 343 LOG (GNUNET_ERROR_TYPE_INFO,
398 _("Postgres database running\n")); 344 "Postgres namecache plugin running\n");
399 return api; 345 return api;
400} 346}
401 347
@@ -416,7 +362,7 @@ libgnunet_plugin_namecache_postgres_done (void *cls)
416 plugin->cfg = NULL; 362 plugin->cfg = NULL;
417 GNUNET_free (api); 363 GNUNET_free (api);
418 LOG (GNUNET_ERROR_TYPE_DEBUG, 364 LOG (GNUNET_ERROR_TYPE_DEBUG,
419 "postgres plugin is finished\n"); 365 "Postgres namecache plugin is finished\n");
420 return NULL; 366 return NULL;
421} 367}
422 368