aboutsummaryrefslogtreecommitdiff
path: root/src/exchangedb/plugin_exchangedb_postgres.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchangedb/plugin_exchangedb_postgres.c')
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c128
1 files changed, 79 insertions, 49 deletions
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 6536bdc35..d06fc6a1e 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -436,7 +436,8 @@ postgres_get_session (void *cls)
436 ",expiration_date" 436 ",expiration_date"
437 ",gc_date" 437 ",gc_date"
438 ") VALUES " 438 ") VALUES "
439 "($1, $2, $3, $4, $5, $6);", 439 "($1, $2, $3, $4, $5, $6)"
440 " ON CONFLICT DO NOTHING;",
440 6), 441 6),
441 /* Used in #postgres_insert_reserve_closed() */ 442 /* Used in #postgres_insert_reserve_closed() */
442 GNUNET_PQ_make_prepare ("reserves_close_insert", 443 GNUNET_PQ_make_prepare ("reserves_close_insert",
@@ -3442,36 +3443,25 @@ postgres_reserves_in_insert (void *cls,
3442 uint64_t wire_ref) 3443 uint64_t wire_ref)
3443{ 3444{
3444 struct PostgresClosure *pg = cls; 3445 struct PostgresClosure *pg = cls;
3445 enum GNUNET_DB_QueryStatus reserve_exists; 3446 enum GNUNET_DB_QueryStatus qs1;
3446 enum GNUNET_DB_QueryStatus qs;
3447 struct TALER_EXCHANGEDB_Reserve reserve; 3447 struct TALER_EXCHANGEDB_Reserve reserve;
3448 struct GNUNET_TIME_Absolute expiry; 3448 struct GNUNET_TIME_Absolute expiry;
3449 3449
3450 reserve.pub = *reserve_pub; 3450 reserve.pub = *reserve_pub;
3451 reserve_exists = postgres_reserves_get (cls, 3451 expiry = GNUNET_TIME_absolute_add (execution_time,
3452 session, 3452 pg->idle_reserve_expiration_time);
3453 &reserve); 3453 (void) GNUNET_TIME_round_abs (&expiry);
3454 if (0 > reserve_exists)
3455 {
3456 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == reserve_exists);
3457 return reserve_exists;
3458 }
3459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3454 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3460 "Creating reserve %s with expiration in %s\n", 3455 "Creating reserve %s with expiration in %s\n",
3461 TALER_B2S (reserve_pub), 3456 TALER_B2S (reserve_pub),
3462 GNUNET_STRINGS_relative_time_to_string ( 3457 GNUNET_STRINGS_relative_time_to_string (
3463 pg->idle_reserve_expiration_time, 3458 pg->idle_reserve_expiration_time,
3464 GNUNET_NO)); 3459 GNUNET_NO));
3465 expiry = GNUNET_TIME_absolute_add (execution_time, 3460 /* Optimistically assume this is a new reserve, create balance for the first
3466 pg->idle_reserve_expiration_time); 3461 time; we do this before adding the actual transaction to "reserves_in",
3467 (void) GNUNET_TIME_round_abs (&expiry); 3462 as for a new reserve it can't be a duplicate 'add' operation, and as
3468 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == reserve_exists) 3463 the 'add' operation may need the reserve entry as a foreign key. */
3469 { 3464 {
3470 /* New reserve, create balance for the first time; we do this
3471 before adding the actual transaction to "reserves_in", as
3472 for a new reserve it can't be a duplicate 'add' operation,
3473 and as the 'add' operation may need the reserve entry
3474 as a foreign key. */
3475 struct GNUNET_PQ_QueryParam params[] = { 3465 struct GNUNET_PQ_QueryParam params[] = {
3476 GNUNET_PQ_query_param_auto_from_type (reserve_pub), 3466 GNUNET_PQ_query_param_auto_from_type (reserve_pub),
3477 GNUNET_PQ_query_param_string (sender_account_details), 3467 GNUNET_PQ_query_param_string (sender_account_details),
@@ -3483,24 +3473,16 @@ postgres_reserves_in_insert (void *cls,
3483 3473
3484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3474 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3485 "Reserve does not exist; creating a new one\n"); 3475 "Reserve does not exist; creating a new one\n");
3486 qs = GNUNET_PQ_eval_prepared_non_select (session->conn, 3476 /* Note: query uses 'on conflict do nothing' */
3487 "reserve_create", 3477 qs1 = GNUNET_PQ_eval_prepared_non_select (session->conn,
3488 params); 3478 "reserve_create",
3489 if (0 > qs) 3479 params);
3490 { 3480 if (qs1 < 0)
3491 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); 3481 return qs1;
3492 return qs;
3493 }
3494 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
3495 {
3496 /* Maybe DB did not detect serializiability error already,
3497 but clearly there must be one. Still odd. */
3498 GNUNET_break (0);
3499 return GNUNET_DB_STATUS_SOFT_ERROR;
3500 }
3501 } 3482 }
3483
3502 /* Create new incoming transaction, "ON CONFLICT DO NOTHING" 3484 /* Create new incoming transaction, "ON CONFLICT DO NOTHING"
3503 is used to guard against duplicates. */ 3485 is again used to guard against duplicates. */
3504 { 3486 {
3505 struct GNUNET_PQ_QueryParam params[] = { 3487 struct GNUNET_PQ_QueryParam params[] = {
3506 GNUNET_PQ_query_param_auto_from_type (&reserve.pub), 3488 GNUNET_PQ_query_param_auto_from_type (&reserve.pub),
@@ -3511,26 +3493,59 @@ postgres_reserves_in_insert (void *cls,
3511 TALER_PQ_query_param_absolute_time (&execution_time), 3493 TALER_PQ_query_param_absolute_time (&execution_time),
3512 GNUNET_PQ_query_param_end 3494 GNUNET_PQ_query_param_end
3513 }; 3495 };
3496 enum GNUNET_DB_QueryStatus qs2;
3514 3497
3515 qs = GNUNET_PQ_eval_prepared_non_select (session->conn, 3498 qs2 = GNUNET_PQ_eval_prepared_non_select (session->conn,
3516 "reserves_in_add_transaction", 3499 "reserves_in_add_transaction",
3517 params); 3500 params);
3518 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 3501 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs2)
3519 { 3502 {
3520 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); 3503 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs2);
3521 return qs; 3504 return qs2;
3505 }
3506 if (0 >= qs2)
3507 {
3508 /* Transaction was already known or error. We are finished. */
3509 return qs2;
3510 }
3511 }
3512 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs1)
3513 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* new reserve, we are finished */
3514
3515 /* we were wrong with our optimistic assumption:
3516 reserve does exist, need to do an update instead */
3517 {
3518 enum GNUNET_DB_QueryStatus reserve_exists;
3519
3520 reserve_exists = postgres_reserves_get (cls,
3521 session,
3522 &reserve);
3523 switch (reserve_exists)
3524 {
3525 case GNUNET_DB_STATUS_HARD_ERROR:
3526 GNUNET_break (0);
3527 return reserve_exists;
3528 case GNUNET_DB_STATUS_SOFT_ERROR:
3529 return reserve_exists;
3530 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
3531 /* First we got a conflict, but then we cannot select? Very strange. */
3532 GNUNET_break (0);
3533 return GNUNET_DB_STATUS_SOFT_ERROR;
3534 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
3535 /* continued below */
3536 break;
3522 } 3537 }
3523 } 3538 }
3524 3539
3525 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == reserve_exists)
3526 { 3540 {
3541 struct TALER_EXCHANGEDB_Reserve updated_reserve;
3542 enum GNUNET_DB_QueryStatus qs3;
3543
3527 /* If the reserve already existed, we need to still update the 3544 /* If the reserve already existed, we need to still update the
3528 balance; we do this after checking for duplication, as 3545 balance; we do this after checking for duplication, as
3529 otherwise we might have to actually pay the cost to roll this 3546 otherwise we might have to actually pay the cost to roll this
3530 back for duplicate transactions; like this, we should virtually 3547 back for duplicate transactions; like this, we should virtually
3531 never actually have to rollback anything. */ 3548 never actually have to rollback anything. */
3532 struct TALER_EXCHANGEDB_Reserve updated_reserve;
3533
3534 updated_reserve.pub = reserve.pub; 3549 updated_reserve.pub = reserve.pub;
3535 if (0 > 3550 if (0 >
3536 TALER_amount_add (&updated_reserve.balance, 3551 TALER_amount_add (&updated_reserve.balance,
@@ -3548,11 +3563,26 @@ postgres_reserves_in_insert (void *cls,
3548 updated_reserve.gc = GNUNET_TIME_absolute_max (updated_reserve.expiry, 3563 updated_reserve.gc = GNUNET_TIME_absolute_max (updated_reserve.expiry,
3549 reserve.gc); 3564 reserve.gc);
3550 (void) GNUNET_TIME_round_abs (&updated_reserve.gc); 3565 (void) GNUNET_TIME_round_abs (&updated_reserve.gc);
3551 return reserves_update (cls, 3566 qs3 = reserves_update (cls,
3552 session, 3567 session,
3553 &updated_reserve); 3568 &updated_reserve);
3569 switch (qs3)
3570 {
3571 case GNUNET_DB_STATUS_HARD_ERROR:
3572 GNUNET_break (0);
3573 return qs3;
3574 case GNUNET_DB_STATUS_SOFT_ERROR:
3575 return qs3;
3576 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
3577 /* How can the UPDATE not work here? Very strange. */
3578 GNUNET_break (0);
3579 return GNUNET_DB_STATUS_HARD_ERROR;
3580 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
3581 /* continued below */
3582 break;
3583 }
3584 return qs3;
3554 } 3585 }
3555 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
3556} 3586}
3557 3587
3558 3588