diff options
-rw-r--r-- | src/namestore/plugin_namestore_postgres.c | 375 | ||||
-rw-r--r-- | src/pq/pq_eval.c | 7 |
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 | */ | ||
243 | struct 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 | */ |
287 | static int | 270 | static void |
288 | get_record_and_call_iterator (struct Plugin *plugin, | 271 | parse_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 | |||
425 | namestore_postgres_iterate_records (void *cls, | 394 | namestore_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 | ||