summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/namestore/plugin_namestore_postgres.c375
-rw-r--r--src/pq/pq_eval.c7
2 files changed, 174 insertions, 208 deletions
diff --git a/src/namestore/plugin_namestore_postgres.c b/src/namestore/plugin_namestore_postgres.c
index 4bf931c93..cea205714 100644
--- a/src/namestore/plugin_namestore_postgres.c
+++ b/src/namestore/plugin_namestore_postgres.c
@@ -32,26 +32,6 @@
32#include "namestore.h" 32#include "namestore.h"
33 33
34 34
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, "namestore-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, "namestore-postgres", __VA_ARGS__) 35#define LOG(kind,...) GNUNET_log_from (kind, "namestore-postgres", __VA_ARGS__)
56 36
57 37
@@ -205,7 +185,7 @@ namestore_postgres_store_records (void *cls,
205 struct Plugin *plugin = cls; 185 struct Plugin *plugin = cls;
206 struct GNUNET_CRYPTO_EcdsaPublicKey pkey; 186 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
207 uint64_t rvalue; 187 uint64_t rvalue;
208 uint32_t rd_count_nbo = htonl ((uint32_t) rd_count); 188 uint32_t rd_count32 = (uint32_t) rd_count;
209 size_t data_size; 189 size_t data_size;
210 190
211 memset (&pkey, 0, sizeof (pkey)); 191 memset (&pkey, 0, sizeof (pkey));
@@ -228,145 +208,134 @@ namestore_postgres_store_records (void *cls,
228 } 208 }
229 { 209 {
230 char data[data_size]; 210 char data[data_size];
231 // FIXME: use libgnunetpq! 211 struct GNUNET_PQ_QueryParam params[] = {
232 const char *paramValues[] = { 212 GNUNET_PQ_query_param_auto_from_type (zone_key),
233 (const char *) zone_key, 213 GNUNET_PQ_query_param_auto_from_type (&pkey),
234 (const char *) &pkey, 214 GNUNET_PQ_query_param_uint64 (&rvalue),
235 (const char *) &rvalue, 215 GNUNET_PQ_query_param_uint32 (&rd_count32),
236 (const char *) &rd_count_nbo, 216 GNUNET_PQ_query_param_fixed_size (data, data_size),
237 (const char *) data, 217 GNUNET_PQ_query_param_string (label),
238 label 218 GNUNET_PQ_query_param_end
239 }; 219 };
240 int paramLengths[] = { 220 enum GNUNET_PQ_QueryStatus res;
241 sizeof (*zone_key),
242 sizeof (pkey),
243 sizeof (rvalue),
244 sizeof (rd_count_nbo),
245 data_size,
246 strlen (label)
247 };
248 const int paramFormats[] = { 1, 1, 1, 1, 1, 1 };
249 PGresult *res;
250 221
251 if (data_size != 222 if (data_size !=
252 GNUNET_GNSRECORD_records_serialize (rd_count, rd, 223 GNUNET_GNSRECORD_records_serialize (rd_count, rd,
253 data_size, data)) 224 data_size, data))
254 { 225 {
255 GNUNET_break (0); 226 GNUNET_break (0);
256 return GNUNET_SYSERR; 227 return GNUNET_SYSERR;
257 } 228 }
258 229
259 res = 230 res = GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
260 PQexecPrepared (plugin->dbh, "store_records", 6, 231 "store_records",
261 paramValues, paramLengths, paramFormats, 1); 232 params);
262 if (GNUNET_OK != 233 if (GNUNET_PQ_STATUS_SUCCESS_NO_RESULTS != res)
263 GNUNET_POSTGRES_check_result (plugin->dbh,
264 res,
265 PGRES_COMMAND_OK,
266 "PQexecPrepared",
267 "store_records"))
268 return GNUNET_SYSERR; 234 return GNUNET_SYSERR;
269 PQclear (res);
270 return GNUNET_OK;
271 } 235 }
236 return GNUNET_OK;
272} 237}
273 238
274 239
275/** 240/**
241 * Closure for #parse_result_call_iterator.
242 */
243struct ParserContext
244{
245 /**
246 * Function to call for each result.
247 */
248 GNUNET_NAMESTORE_RecordIterator iter;
249
250 /**
251 * Closure for @e iter.
252 */
253 void *iter_cls;
254
255 /**
256 * Zone key, NULL if part of record.
257 */
258 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key;
259};
260
261
262/**
276 * A statement has been run. We should evaluate the result, and if possible 263 * A statement has been run. We should evaluate the result, and if possible
277 * call the given @a iter with the result. 264 * call the @a iter in @a cls with the result.
278 * 265 *
279 * @param plugin plugin context 266 * @param cls closure of type `struct ParserContext *`
280 * @param res result from the statement that was run (to be cleaned up) 267 * @param result the postgres result
281 * @param zone_key private key of the zone, could be NULL, in which case we should 268 * @param num_result the number of results in @a result
282 * get the zone from @a res
283 * @param iter iterator to call with the result
284 * @param iter_cls closure for @a iter
285 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
286 */ 269 */
287static int 270static void
288get_record_and_call_iterator (struct Plugin *plugin, 271parse_result_call_iterator (void *cls,
289 PGresult *res, 272 PGresult *res,
290 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, 273 unsigned int num_results)
291 GNUNET_NAMESTORE_RecordIterator iter,
292 void *iter_cls)
293{ 274{
294 const char *data; 275 struct ParserContext *pc = cls;
295 size_t data_size; 276
296 uint32_t record_count; 277 for (unsigned int i=0;i<num_results;i++)
297 const char *label;
298 size_t label_len;
299 unsigned int cnt;
300
301 if (GNUNET_OK !=
302 GNUNET_POSTGRES_check_result (plugin->dbh,
303 res,
304 PGRES_TUPLES_OK,
305 "PQexecPrepared",
306 "iteration"))
307 {
308 LOG (GNUNET_ERROR_TYPE_DEBUG,
309 "Failing lookup (postgres error)\n");
310 return GNUNET_SYSERR;
311 }
312 if (0 == (cnt = PQntuples (res)))
313 {
314 /* no result */
315 LOG (GNUNET_ERROR_TYPE_DEBUG,
316 "Ending iteration (no more results)\n");
317 PQclear (res);
318 return GNUNET_NO;
319 }
320 GNUNET_assert (1 == cnt);
321 GNUNET_assert (3 + ((NULL == zone_key) ? 1 : 0) == PQnfields (res));
322 if (NULL == zone_key)
323 { 278 {
324 if (sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey) != PQgetlength (res, 0, 3)) 279 void *data;
280 size_t data_size;
281 uint32_t record_count;
282 char *label;
283 struct GNUNET_CRYPTO_EcdsaPrivateKey zk;
284 struct GNUNET_PQ_ResultSpec rs_with_zone[] = {
285 GNUNET_PQ_result_spec_uint32 ("record_count", &record_count),
286 GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size),
287 GNUNET_PQ_result_spec_string ("label", &label),
288 GNUNET_PQ_result_spec_auto_from_type ("zone_private_key", &zk),
289 GNUNET_PQ_result_spec_end
290 };
291 struct GNUNET_PQ_ResultSpec rs_without_zone[] = {
292 GNUNET_PQ_result_spec_uint32 ("record_count", &record_count),
293 GNUNET_PQ_result_spec_variable_size ("record_data", &data, &data_size),
294 GNUNET_PQ_result_spec_string ("label", &label),
295 GNUNET_PQ_result_spec_end
296 };
297 struct GNUNET_PQ_ResultSpec *rs;
298
299 rs = (NULL == pc->zone_key) ? rs_with_zone : rs_without_zone;
300 if (GNUNET_YES !=
301 GNUNET_PQ_extract_result (res,
302 rs,
303 i))
325 { 304 {
326 GNUNET_break (0); 305 GNUNET_break (0);
327 PQclear (res); 306 return;
328 return GNUNET_SYSERR;
329 } 307 }
330 zone_key = (const struct GNUNET_CRYPTO_EcdsaPrivateKey *) PQgetvalue (res, 0, 3);
331 }
332 if (sizeof (uint32_t) != PQfsize (res, 0))
333 {
334 GNUNET_break (0);
335 PQclear (res);
336 return GNUNET_SYSERR;
337 }
338 308
339 record_count = ntohl (*(uint32_t *) PQgetvalue (res, 0, 0)); 309 if (record_count > 64 * 1024)
340 data = PQgetvalue (res, 0, 1);
341 data_size = PQgetlength (res, 0, 1);
342 label = PQgetvalue (res, 0, 2);
343 label_len = PQgetlength (res, 0, 1);
344 if (record_count > 64 * 1024)
345 {
346 /* sanity check, don't stack allocate far too much just
347 because database might contain a large value here */
348 GNUNET_break (0);
349 PQclear (res);
350 return GNUNET_SYSERR;
351 }
352 {
353 struct GNUNET_GNSRECORD_Data rd[record_count];
354 char buf[label_len + 1];
355
356 GNUNET_memcpy (buf, label, label_len);
357 buf[label_len] = '\0';
358 if (GNUNET_OK !=
359 GNUNET_GNSRECORD_records_deserialize (data_size, data,
360 record_count, rd))
361 { 310 {
311 /* sanity check, don't stack allocate far too much just
312 because database might contain a large value here */
362 GNUNET_break (0); 313 GNUNET_break (0);
363 PQclear (res); 314 GNUNET_PQ_cleanup_result (rs);
364 return GNUNET_SYSERR; 315 return;
365 } 316 }
366 iter (iter_cls, zone_key, buf, record_count, rd); 317
318 {
319 struct GNUNET_GNSRECORD_Data rd[record_count];
320
321 if (GNUNET_OK !=
322 GNUNET_GNSRECORD_records_deserialize (data_size,
323 data,
324 record_count,
325 rd))
326 {
327 GNUNET_break (0);
328 GNUNET_PQ_cleanup_result (rs);
329 return;
330 }
331 pc->iter (pc->iter_cls,
332 (NULL == pc->zone_key) ? &zk : pc->zone_key,
333 label,
334 record_count,
335 rd);
336 }
337 GNUNET_PQ_cleanup_result (rs);
367 } 338 }
368 PQclear (res);
369 return GNUNET_OK;
370} 339}
371 340
372 341
@@ -388,25 +357,25 @@ namestore_postgres_lookup_records (void *cls,
388 void *iter_cls) 357 void *iter_cls)
389{ 358{
390 struct Plugin *plugin = cls; 359 struct Plugin *plugin = cls;
391 const char *paramValues[] = { 360 struct GNUNET_PQ_QueryParam params[] = {
392 (const char *) zone, 361 GNUNET_PQ_query_param_auto_from_type (zone),
393 label 362 GNUNET_PQ_query_param_string (label),
394 }; 363 GNUNET_PQ_query_param_end
395 int paramLengths[] = {
396 sizeof (*zone),
397 strlen (label)
398 }; 364 };
399 const int paramFormats[] = { 1, 1 }; 365 struct ParserContext pc;
400 PGresult *res; 366 enum GNUNET_PQ_QueryStatus res;
401 367
402 res = PQexecPrepared (plugin->dbh, 368 pc.iter = iter;
403 "lookup_label", 2, 369 pc.iter_cls = iter_cls;
404 paramValues, paramLengths, paramFormats, 370 pc.zone_key = NULL;
405 1); 371 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
406 return get_record_and_call_iterator (plugin, 372 "lookup_label",
407 res, 373 params,
408 zone, 374 &parse_result_call_iterator,
409 iter, iter_cls); 375 &pc);
376 if (res < 0)
377 return GNUNET_SYSERR;
378 return GNUNET_OK;
410} 379}
411 380
412 381
@@ -425,53 +394,46 @@ static int
425namestore_postgres_iterate_records (void *cls, 394namestore_postgres_iterate_records (void *cls,
426 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, 395 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
427 uint64_t offset, 396 uint64_t offset,
428 GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls) 397 GNUNET_NAMESTORE_RecordIterator iter,
398 void *iter_cls)
429{ 399{
430 struct Plugin *plugin = cls; 400 struct Plugin *plugin = cls;
431 uint64_t offset_be = GNUNET_htonll (offset); 401 enum GNUNET_PQ_QueryStatus res;
402 struct ParserContext pc;
432 403
404 pc.iter = iter;
405 pc.iter_cls = iter_cls;
406 pc.zone_key = zone;
433 if (NULL == zone) 407 if (NULL == zone)
434 { 408 {
435 const char *paramValues[] = { 409 struct GNUNET_PQ_QueryParam params_without_zone[] = {
436 (const char *) &offset_be 410 GNUNET_PQ_query_param_uint64 (&offset),
411 GNUNET_PQ_query_param_end
437 }; 412 };
438 int paramLengths[] = { 413
439 sizeof (offset_be) 414 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
440 }; 415 "iterate_all_zones",
441 const int paramFormats[] = { 1 }; 416 params_without_zone,
442 PGresult *res; 417 &parse_result_call_iterator,
443 418 &pc);
444 res = PQexecPrepared (plugin->dbh,
445 "iterate_all_zones", 1,
446 paramValues, paramLengths, paramFormats,
447 1);
448 return get_record_and_call_iterator (plugin,
449 res,
450 NULL,
451 iter, iter_cls);
452 } 419 }
453 else 420 else
454 { 421 {
455 const char *paramValues[] = { 422 struct GNUNET_PQ_QueryParam params_with_zone[] = {
456 (const char *) zone, 423 GNUNET_PQ_query_param_auto_from_type (zone),
457 (const char *) &offset_be 424 GNUNET_PQ_query_param_uint64 (&offset),
458 }; 425 GNUNET_PQ_query_param_end
459 int paramLengths[] = {
460 sizeof (*zone),
461 sizeof (offset_be)
462 }; 426 };
463 const int paramFormats[] = { 1, 1 }; 427
464 PGresult *res; 428 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
465 429 "iterate_zone",
466 res = PQexecPrepared (plugin->dbh, 430 params_with_zone,
467 "iterate_zone", 2, 431 &parse_result_call_iterator,
468 paramValues, paramLengths, paramFormats, 432 &pc);
469 1);
470 return get_record_and_call_iterator (plugin,
471 res,
472 zone,
473 iter, iter_cls);
474 } 433 }
434 if (res < 0)
435 return GNUNET_SYSERR;
436 return GNUNET_OK;
475} 437}
476 438
477 439
@@ -493,25 +455,26 @@ namestore_postgres_zone_to_name (void *cls,
493 GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls) 455 GNUNET_NAMESTORE_RecordIterator iter, void *iter_cls)
494{ 456{
495 struct Plugin *plugin = cls; 457 struct Plugin *plugin = cls;
496 const char *paramValues[] = { 458 struct GNUNET_PQ_QueryParam params[] = {
497 (const char *) zone, 459 GNUNET_PQ_query_param_auto_from_type (zone),
498 (const char *) value_zone 460 GNUNET_PQ_query_param_auto_from_type (value_zone),
499 }; 461 GNUNET_PQ_query_param_end
500 int paramLengths[] = {
501 sizeof (*zone),
502 sizeof (*value_zone)
503 }; 462 };
504 const int paramFormats[] = { 1, 1 }; 463 enum GNUNET_PQ_QueryStatus res;
505 PGresult *res; 464 struct ParserContext pc;
506 465
507 res = PQexecPrepared (plugin->dbh, 466 pc.iter = iter;
508 "zone_to_name", 2, 467 pc.iter_cls = iter_cls;
509 paramValues, paramLengths, paramFormats, 468 pc.zone_key = zone;
510 1); 469
511 return get_record_and_call_iterator (plugin, 470 res = GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
512 res, 471 "zone_to_name",
513 zone, 472 params,
514 iter, iter_cls); 473 &parse_result_call_iterator,
474 &pc);
475 if (res < 0)
476 return GNUNET_SYSERR;
477 return GNUNET_OK;
515} 478}
516 479
517 480
@@ -558,7 +521,7 @@ libgnunet_plugin_namestore_postgres_init (void *cls)
558 api->zone_to_name = &namestore_postgres_zone_to_name; 521 api->zone_to_name = &namestore_postgres_zone_to_name;
559 api->lookup_records = &namestore_postgres_lookup_records; 522 api->lookup_records = &namestore_postgres_lookup_records;
560 LOG (GNUNET_ERROR_TYPE_INFO, 523 LOG (GNUNET_ERROR_TYPE_INFO,
561 _("Postgres database running\n")); 524 "Postgres namestore plugin running\n");
562 return api; 525 return api;
563} 526}
564 527
@@ -579,7 +542,7 @@ libgnunet_plugin_namestore_postgres_done (void *cls)
579 plugin->cfg = NULL; 542 plugin->cfg = NULL;
580 GNUNET_free (api); 543 GNUNET_free (api);
581 LOG (GNUNET_ERROR_TYPE_DEBUG, 544 LOG (GNUNET_ERROR_TYPE_DEBUG,
582 "postgres plugin is finished\n"); 545 "Postgres namestore plugin is finished\n");
583 return NULL; 546 return NULL;
584} 547}
585 548
diff --git a/src/pq/pq_eval.c b/src/pq/pq_eval.c
index 39a7a2c98..d6c10e2c5 100644
--- a/src/pq/pq_eval.c
+++ b/src/pq/pq_eval.c
@@ -57,8 +57,11 @@ GNUNET_PQ_eval_result (PGconn *connection,
57 const char *statement_name, 57 const char *statement_name,
58 PGresult *result) 58 PGresult *result)
59{ 59{
60 if (PGRES_COMMAND_OK != 60 ExecStatusType est;
61 PQresultStatus (result)) 61
62 est = PQresultStatus (result);
63 if ( (PGRES_COMMAND_OK != est) &&
64 (PGRES_TUPLES_OK != est) )
62 { 65 {
63 const char *sqlstate; 66 const char *sqlstate;
64 67