LCOV - code coverage report
Current view: top level - exchangedb - pg_get_coin_transactions.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 182 364 50.0 %
Date: 2025-06-05 21:03:14 Functions: 6 11 54.5 %

          Line data    Source code
       1             : /*
       2             :    This file is part of TALER
       3             :    Copyright (C) 2022-2023 Taler Systems SA
       4             : 
       5             :    TALER is free software; you can redistribute it and/or modify it under the
       6             :    terms of the GNU General Public License as published by the Free Software
       7             :    Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :    TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :    You should have received a copy of the GNU General Public License along with
      14             :    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             :  */
      16             : /**
      17             :  * @file pg_get_coin_transactions.c
      18             :  * @brief Low-level (statement-level) Postgres database access for the exchange
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include "taler_error_codes.h"
      23             : #include "taler_dbevents.h"
      24             : #include "taler_exchangedb_plugin.h"
      25             : #include "taler_pq_lib.h"
      26             : #include "pg_get_coin_transactions.h"
      27             : #include "pg_helper.h"
      28             : #include "pg_start_read_committed.h"
      29             : #include "pg_commit.h"
      30             : #include "pg_rollback.h"
      31             : #include "plugin_exchangedb_common.h"
      32             : 
      33             : /**
      34             :  * How often do we re-try when encountering DB serialization issues?
      35             :  * (We are read-only, so can only happen due to concurrent insert,
      36             :  * which should be very rare.)
      37             :  */
      38             : #define RETRIES 3
      39             : 
      40             : /**
      41             :  * Closure for callbacks called from #TEH_PG_get_coin_transactions()
      42             :  */
      43             : struct CoinHistoryContext
      44             : {
      45             :   /**
      46             :    * Head of the coin's history list.
      47             :    */
      48             :   struct TALER_EXCHANGEDB_TransactionList *head;
      49             : 
      50             :   /**
      51             :    * Public key of the coin we are building the history for.
      52             :    */
      53             :   const struct TALER_CoinSpendPublicKeyP *coin_pub;
      54             : 
      55             :   /**
      56             :    * Plugin context.
      57             :    */
      58             :   struct PostgresClosure *pg;
      59             : 
      60             :   /**
      61             :    * Set to 'true' if the transaction failed.
      62             :    */
      63             :   bool failed;
      64             : 
      65             : };
      66             : 
      67             : 
      68             : /**
      69             :  * Function to be called with the results of a SELECT statement
      70             :  * that has returned @a num_results results.
      71             :  *
      72             :  * @param cls closure of type `struct CoinHistoryContext`
      73             :  * @param result the postgres result
      74             :  * @param num_results the number of results in @a result
      75             :  */
      76             : static void
      77         146 : add_coin_deposit (void *cls,
      78             :                   PGresult *result,
      79             :                   unsigned int num_results)
      80             : {
      81         146 :   struct CoinHistoryContext *chc = cls;
      82         146 :   struct PostgresClosure *pg = chc->pg;
      83             : 
      84         289 :   for (unsigned int i = 0; i < num_results; i++)
      85             :   {
      86             :     struct TALER_EXCHANGEDB_DepositListEntry *deposit;
      87             :     struct TALER_EXCHANGEDB_TransactionList *tl;
      88             :     uint64_t serial_id;
      89             : 
      90         143 :     deposit = GNUNET_new (struct TALER_EXCHANGEDB_DepositListEntry);
      91             :     {
      92         143 :       struct GNUNET_PQ_ResultSpec rs[] = {
      93         143 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
      94             :                                      &deposit->amount_with_fee),
      95         143 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
      96             :                                      &deposit->deposit_fee),
      97         143 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
      98             :                                               &deposit->h_denom_pub),
      99         143 :         GNUNET_PQ_result_spec_allow_null (
     100         143 :           GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
     101             :                                                 &deposit->h_age_commitment),
     102             :           &deposit->no_age_commitment),
     103         143 :         GNUNET_PQ_result_spec_allow_null (
     104         143 :           GNUNET_PQ_result_spec_auto_from_type ("wallet_data_hash",
     105             :                                                 &deposit->wallet_data_hash),
     106             :           &deposit->no_wallet_data_hash),
     107         143 :         GNUNET_PQ_result_spec_timestamp ("wallet_timestamp",
     108             :                                          &deposit->timestamp),
     109         143 :         GNUNET_PQ_result_spec_timestamp ("refund_deadline",
     110             :                                          &deposit->refund_deadline),
     111         143 :         GNUNET_PQ_result_spec_timestamp ("wire_deadline",
     112             :                                          &deposit->wire_deadline),
     113         143 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
     114             :                                               &deposit->merchant_pub),
     115         143 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
     116             :                                               &deposit->h_contract_terms),
     117         143 :         GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
     118             :                                               &deposit->wire_salt),
     119         143 :         GNUNET_PQ_result_spec_string ("payto_uri",
     120             :                                       &deposit->receiver_wire_account.full_payto
     121             :                                       ),
     122         143 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     123             :                                               &deposit->csig),
     124         143 :         GNUNET_PQ_result_spec_uint64 ("coin_deposit_serial_id",
     125             :                                       &serial_id),
     126         143 :         GNUNET_PQ_result_spec_auto_from_type ("done",
     127             :                                               &deposit->done),
     128             :         GNUNET_PQ_result_spec_end
     129             :       };
     130             : 
     131         143 :       if (GNUNET_OK !=
     132         143 :           GNUNET_PQ_extract_result (result,
     133             :                                     rs,
     134             :                                     i))
     135             :       {
     136           0 :         GNUNET_break (0);
     137           0 :         GNUNET_free (deposit);
     138           0 :         chc->failed = true;
     139           0 :         return;
     140             :       }
     141             :     }
     142         143 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     143         143 :     tl->next = chc->head;
     144         143 :     tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
     145         143 :     tl->details.deposit = deposit;
     146         143 :     tl->serial_id = serial_id;
     147         143 :     chc->head = tl;
     148             :   }
     149             : }
     150             : 
     151             : 
     152             : /**
     153             :  * Function to be called with the results of a SELECT statement
     154             :  * that has returned @a num_results results.
     155             :  *
     156             :  * @param cls closure of type `struct CoinHistoryContext`
     157             :  * @param result the postgres result
     158             :  * @param num_results the number of results in @a result
     159             :  */
     160             : static void
     161           3 : add_coin_purse_deposit (void *cls,
     162             :                         PGresult *result,
     163             :                         unsigned int num_results)
     164             : {
     165           3 :   struct CoinHistoryContext *chc = cls;
     166           3 :   struct PostgresClosure *pg = chc->pg;
     167             : 
     168           6 :   for (unsigned int i = 0; i < num_results; i++)
     169             :   {
     170             :     struct TALER_EXCHANGEDB_PurseDepositListEntry *deposit;
     171             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     172             :     uint64_t serial_id;
     173             : 
     174           3 :     deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry);
     175             :     {
     176             :       bool not_finished;
     177           3 :       struct GNUNET_PQ_ResultSpec rs[] = {
     178           3 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
     179             :                                      &deposit->amount),
     180           3 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
     181             :                                      &deposit->deposit_fee),
     182           3 :         GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
     183             :                                               &deposit->purse_pub),
     184           3 :         GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
     185             :                                       &serial_id),
     186           3 :         GNUNET_PQ_result_spec_allow_null (
     187             :           GNUNET_PQ_result_spec_string ("partner_base_url",
     188             :                                         &deposit->exchange_base_url),
     189             :           NULL),
     190           3 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     191             :                                               &deposit->coin_sig),
     192           3 :         GNUNET_PQ_result_spec_allow_null (
     193           3 :           GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
     194             :                                                 &deposit->h_age_commitment),
     195             :           &deposit->no_age_commitment),
     196           3 :         GNUNET_PQ_result_spec_allow_null (
     197             :           GNUNET_PQ_result_spec_bool ("refunded",
     198             :                                       &deposit->refunded),
     199             :           &not_finished),
     200             :         GNUNET_PQ_result_spec_end
     201             :       };
     202             : 
     203           3 :       if (GNUNET_OK !=
     204           3 :           GNUNET_PQ_extract_result (result,
     205             :                                     rs,
     206             :                                     i))
     207             :       {
     208           0 :         GNUNET_break (0);
     209           0 :         GNUNET_free (deposit);
     210           0 :         chc->failed = true;
     211           0 :         return;
     212             :       }
     213           3 :       if (not_finished)
     214           1 :         deposit->refunded = false;
     215           3 :       deposit->no_age_commitment = GNUNET_is_zero (&deposit->h_age_commitment);
     216             :     }
     217           3 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     218           3 :     tl->next = chc->head;
     219           3 :     tl->type = TALER_EXCHANGEDB_TT_PURSE_DEPOSIT;
     220           3 :     tl->details.purse_deposit = deposit;
     221           3 :     tl->serial_id = serial_id;
     222           3 :     chc->head = tl;
     223             :   }
     224             : }
     225             : 
     226             : 
     227             : /**
     228             :  * Function to be called with the results of a SELECT statement
     229             :  * that has returned @a num_results results.
     230             :  *
     231             :  * @param cls closure of type `struct CoinHistoryContext`
     232             :  * @param result the postgres result
     233             :  * @param num_results the number of results in @a result
     234             :  */
     235             : static void
     236         192 : add_coin_melt (void *cls,
     237             :                PGresult *result,
     238             :                unsigned int num_results)
     239             : {
     240         192 :   struct CoinHistoryContext *chc = cls;
     241         192 :   struct PostgresClosure *pg = chc->pg;
     242             : 
     243         384 :   for (unsigned int i = 0; i<num_results; i++)
     244             :   {
     245             :     struct TALER_EXCHANGEDB_MeltListEntry *melt;
     246             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     247             :     uint64_t serial_id;
     248             : 
     249         192 :     melt = GNUNET_new (struct TALER_EXCHANGEDB_MeltListEntry);
     250             :     {
     251         192 :       struct GNUNET_PQ_ResultSpec rs[] = {
     252         192 :         GNUNET_PQ_result_spec_auto_from_type ("rc",
     253             :                                               &melt->rc),
     254             :         /* oldcoin_index not needed */
     255         192 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     256             :                                               &melt->h_denom_pub),
     257         192 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
     258             :                                               &melt->coin_sig),
     259         192 :         GNUNET_PQ_result_spec_auto_from_type ("refresh_seed",
     260             :                                               &melt->refresh_seed),
     261         192 :         GNUNET_PQ_result_spec_allow_null (
     262         192 :           GNUNET_PQ_result_spec_auto_from_type ("blinding_seed",
     263             :                                                 &melt->blinding_seed),
     264             :           &melt->no_blinding_seed),
     265         192 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
     266             :                                      &melt->amount_with_fee),
     267         192 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
     268             :                                      &melt->melt_fee),
     269         192 :         GNUNET_PQ_result_spec_allow_null (
     270         192 :           GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
     271             :                                                 &melt->h_age_commitment),
     272             :           &melt->no_age_commitment),
     273         192 :         GNUNET_PQ_result_spec_uint64 ("refresh_id",
     274             :                                       &serial_id),
     275             :         GNUNET_PQ_result_spec_end
     276             :       };
     277             : 
     278         192 :       if (GNUNET_OK !=
     279         192 :           GNUNET_PQ_extract_result (result,
     280             :                                     rs,
     281             :                                     i))
     282             :       {
     283           0 :         GNUNET_break (0);
     284           0 :         GNUNET_free (melt);
     285           0 :         chc->failed = true;
     286           0 :         return;
     287             :       }
     288             :     }
     289         192 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     290         192 :     tl->next = chc->head;
     291         192 :     tl->type = TALER_EXCHANGEDB_TT_MELT;
     292         192 :     tl->details.melt = melt;
     293         192 :     tl->serial_id = serial_id;
     294         192 :     chc->head = tl;
     295             :   }
     296             : }
     297             : 
     298             : 
     299             : /**
     300             :  * Function to be called with the results of a SELECT statement
     301             :  * that has returned @a num_results results.
     302             :  *
     303             :  * @param cls closure of type `struct CoinHistoryContext`
     304             :  * @param result the postgres result
     305             :  * @param num_results the number of results in @a result
     306             :  */
     307             : static void
     308          48 : add_coin_refund (void *cls,
     309             :                  PGresult *result,
     310             :                  unsigned int num_results)
     311             : {
     312          48 :   struct CoinHistoryContext *chc = cls;
     313          48 :   struct PostgresClosure *pg = chc->pg;
     314             : 
     315          93 :   for (unsigned int i = 0; i<num_results; i++)
     316             :   {
     317             :     struct TALER_EXCHANGEDB_RefundListEntry *refund;
     318             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     319             :     uint64_t serial_id;
     320             : 
     321          45 :     refund = GNUNET_new (struct TALER_EXCHANGEDB_RefundListEntry);
     322             :     {
     323          45 :       struct GNUNET_PQ_ResultSpec rs[] = {
     324          45 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
     325             :                                               &refund->merchant_pub),
     326          45 :         GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
     327             :                                               &refund->merchant_sig),
     328          45 :         GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
     329             :                                               &refund->h_contract_terms),
     330          45 :         GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
     331             :                                       &refund->rtransaction_id),
     332          45 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
     333             :                                      &refund->refund_amount),
     334          45 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
     335             :                                      &refund->refund_fee),
     336          45 :         GNUNET_PQ_result_spec_uint64 ("refund_serial_id",
     337             :                                       &serial_id),
     338             :         GNUNET_PQ_result_spec_end
     339             :       };
     340             : 
     341          45 :       if (GNUNET_OK !=
     342          45 :           GNUNET_PQ_extract_result (result,
     343             :                                     rs,
     344             :                                     i))
     345             :       {
     346           0 :         GNUNET_break (0);
     347           0 :         GNUNET_free (refund);
     348           0 :         chc->failed = true;
     349           0 :         return;
     350             :       }
     351             :     }
     352          45 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     353          45 :     tl->next = chc->head;
     354          45 :     tl->type = TALER_EXCHANGEDB_TT_REFUND;
     355          45 :     tl->details.refund = refund;
     356          45 :     tl->serial_id = serial_id;
     357          45 :     chc->head = tl;
     358             :   }
     359             : }
     360             : 
     361             : 
     362             : /**
     363             :  * Function to be called with the results of a SELECT statement
     364             :  * that has returned @a num_results results.
     365             :  *
     366             :  * @param cls closure of type `struct CoinHistoryContext`
     367             :  * @param result the postgres result
     368             :  * @param num_results the number of results in @a result
     369             :  */
     370             : static void
     371           0 : add_coin_purse_decision (void *cls,
     372             :                          PGresult *result,
     373             :                          unsigned int num_results)
     374             : {
     375           0 :   struct CoinHistoryContext *chc = cls;
     376           0 :   struct PostgresClosure *pg = chc->pg;
     377             : 
     378           0 :   for (unsigned int i = 0; i<num_results; i++)
     379             :   {
     380             :     struct TALER_EXCHANGEDB_PurseRefundListEntry *prefund;
     381             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     382             :     uint64_t serial_id;
     383             : 
     384           0 :     prefund = GNUNET_new (struct TALER_EXCHANGEDB_PurseRefundListEntry);
     385             :     {
     386           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     387           0 :         GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
     388             :                                               &prefund->purse_pub),
     389           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
     390             :                                      &prefund->refund_amount),
     391           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund",
     392             :                                      &prefund->refund_fee),
     393           0 :         GNUNET_PQ_result_spec_uint64 ("purse_decision_serial_id",
     394             :                                       &serial_id),
     395             :         GNUNET_PQ_result_spec_end
     396             :       };
     397             : 
     398           0 :       if (GNUNET_OK !=
     399           0 :           GNUNET_PQ_extract_result (result,
     400             :                                     rs,
     401             :                                     i))
     402             :       {
     403           0 :         GNUNET_break (0);
     404           0 :         GNUNET_free (prefund);
     405           0 :         chc->failed = true;
     406           0 :         return;
     407             :       }
     408             :     }
     409           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     410           0 :     tl->next = chc->head;
     411           0 :     tl->type = TALER_EXCHANGEDB_TT_PURSE_REFUND;
     412           0 :     tl->details.purse_refund = prefund;
     413           0 :     tl->serial_id = serial_id;
     414           0 :     chc->head = tl;
     415             :   }
     416             : }
     417             : 
     418             : 
     419             : /**
     420             :  * Function to be called with the results of a SELECT statement
     421             :  * that has returned @a num_results results.
     422             :  *
     423             :  * @param cls closure of type `struct CoinHistoryContext`
     424             :  * @param result the postgres result
     425             :  * @param num_results the number of results in @a result
     426             :  */
     427             : static void
     428           0 : add_old_coin_recoup (void *cls,
     429             :                      PGresult *result,
     430             :                      unsigned int num_results)
     431             : {
     432           0 :   struct CoinHistoryContext *chc = cls;
     433           0 :   struct PostgresClosure *pg = chc->pg;
     434             : 
     435           0 :   for (unsigned int i = 0; i<num_results; i++)
     436             :   {
     437             :     struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
     438             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     439             :     uint64_t serial_id;
     440             : 
     441           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
     442             :     {
     443           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     444           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
     445             :                                               &recoup->coin.coin_pub),
     446           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     447             :                                               &recoup->coin_sig),
     448           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
     449             :                                               &recoup->coin_blind),
     450           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
     451             :                                      &recoup->value),
     452           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
     453             :                                          &recoup->timestamp),
     454           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     455             :                                               &recoup->coin.denom_pub_hash),
     456           0 :         TALER_PQ_result_spec_denom_sig ("denom_sig",
     457             :                                         &recoup->coin.denom_sig),
     458           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
     459             :                                       &serial_id),
     460             :         GNUNET_PQ_result_spec_end
     461             :       };
     462             : 
     463           0 :       if (GNUNET_OK !=
     464           0 :           GNUNET_PQ_extract_result (result,
     465             :                                     rs,
     466             :                                     i))
     467             :       {
     468           0 :         GNUNET_break (0);
     469           0 :         GNUNET_free (recoup);
     470           0 :         chc->failed = true;
     471           0 :         return;
     472             :       }
     473           0 :       recoup->old_coin_pub = *chc->coin_pub;
     474             :     }
     475           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     476           0 :     tl->next = chc->head;
     477           0 :     tl->type = TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP;
     478           0 :     tl->details.old_coin_recoup = recoup;
     479           0 :     tl->serial_id = serial_id;
     480           0 :     chc->head = tl;
     481             :   }
     482             : }
     483             : 
     484             : 
     485             : /**
     486             :  * Function to be called with the results of a SELECT statement
     487             :  * that has returned @a num_results results.
     488             :  *
     489             :  * @param cls closure of type `struct CoinHistoryContext`
     490             :  * @param result the postgres result
     491             :  * @param num_results the number of results in @a result
     492             :  */
     493             : static void
     494           0 : add_coin_recoup (void *cls,
     495             :                  PGresult *result,
     496             :                  unsigned int num_results)
     497             : {
     498           0 :   struct CoinHistoryContext *chc = cls;
     499           0 :   struct PostgresClosure *pg = chc->pg;
     500             : 
     501           0 :   for (unsigned int i = 0; i<num_results; i++)
     502             :   {
     503             :     struct TALER_EXCHANGEDB_RecoupListEntry *recoup;
     504             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     505             :     uint64_t serial_id;
     506             : 
     507           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupListEntry);
     508             :     {
     509           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     510           0 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
     511             :                                               &recoup->reserve_pub),
     512           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     513             :                                               &recoup->coin_sig),
     514           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     515             :                                               &recoup->h_denom_pub),
     516           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
     517             :                                               &recoup->coin_blind),
     518           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
     519             :                                      &recoup->value),
     520           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
     521             :                                          &recoup->timestamp),
     522           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_uuid",
     523             :                                       &serial_id),
     524             :         GNUNET_PQ_result_spec_end
     525             :       };
     526             : 
     527           0 :       if (GNUNET_OK !=
     528           0 :           GNUNET_PQ_extract_result (result,
     529             :                                     rs,
     530             :                                     i))
     531             :       {
     532           0 :         GNUNET_break (0);
     533           0 :         GNUNET_free (recoup);
     534           0 :         chc->failed = true;
     535           0 :         return;
     536             :       }
     537             :     }
     538           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     539           0 :     tl->next = chc->head;
     540           0 :     tl->type = TALER_EXCHANGEDB_TT_RECOUP;
     541           0 :     tl->details.recoup = recoup;
     542           0 :     tl->serial_id = serial_id;
     543           0 :     chc->head = tl;
     544             :   }
     545             : }
     546             : 
     547             : 
     548             : /**
     549             :  * Function to be called with the results of a SELECT statement
     550             :  * that has returned @a num_results results.
     551             :  *
     552             :  * @param cls closure of type `struct CoinHistoryContext`
     553             :  * @param result the postgres result
     554             :  * @param num_results the number of results in @a result
     555             :  */
     556             : static void
     557           0 : add_coin_recoup_refresh (void *cls,
     558             :                          PGresult *result,
     559             :                          unsigned int num_results)
     560             : {
     561           0 :   struct CoinHistoryContext *chc = cls;
     562           0 :   struct PostgresClosure *pg = chc->pg;
     563             : 
     564           0 :   for (unsigned int i = 0; i<num_results; i++)
     565             :   {
     566             :     struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup;
     567             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     568             :     uint64_t serial_id;
     569             : 
     570           0 :     recoup = GNUNET_new (struct TALER_EXCHANGEDB_RecoupRefreshListEntry);
     571             :     {
     572           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     573           0 :         GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
     574             :                                               &recoup->old_coin_pub),
     575           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     576             :                                               &recoup->coin_sig),
     577           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_blind",
     578             :                                               &recoup->coin_blind),
     579           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
     580             :                                      &recoup->value),
     581           0 :         GNUNET_PQ_result_spec_timestamp ("recoup_timestamp",
     582             :                                          &recoup->timestamp),
     583           0 :         GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
     584             :                                               &recoup->coin.denom_pub_hash),
     585           0 :         TALER_PQ_result_spec_denom_sig ("denom_sig",
     586             :                                         &recoup->coin.denom_sig),
     587           0 :         GNUNET_PQ_result_spec_uint64 ("recoup_refresh_uuid",
     588             :                                       &serial_id),
     589             :         GNUNET_PQ_result_spec_end
     590             :       };
     591             : 
     592           0 :       if (GNUNET_OK !=
     593           0 :           GNUNET_PQ_extract_result (result,
     594             :                                     rs,
     595             :                                     i))
     596             :       {
     597           0 :         GNUNET_break (0);
     598           0 :         GNUNET_free (recoup);
     599           0 :         chc->failed = true;
     600           0 :         return;
     601             :       }
     602           0 :       recoup->coin.coin_pub = *chc->coin_pub;
     603             :     }
     604           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     605           0 :     tl->next = chc->head;
     606           0 :     tl->type = TALER_EXCHANGEDB_TT_RECOUP_REFRESH;
     607           0 :     tl->details.recoup_refresh = recoup;
     608           0 :     tl->serial_id = serial_id;
     609           0 :     chc->head = tl;
     610             :   }
     611             : }
     612             : 
     613             : 
     614             : /**
     615             :  * Function to be called with the results of a SELECT statement
     616             :  * that has returned @a num_results results.
     617             :  *
     618             :  * @param cls closure of type `struct CoinHistoryContext`
     619             :  * @param result the postgres result
     620             :  * @param num_results the number of results in @a result
     621             :  */
     622             : static void
     623           0 : add_coin_reserve_open (void *cls,
     624             :                        PGresult *result,
     625             :                        unsigned int num_results)
     626             : {
     627           0 :   struct CoinHistoryContext *chc = cls;
     628           0 :   struct PostgresClosure *pg = chc->pg;
     629             : 
     630           0 :   for (unsigned int i = 0; i<num_results; i++)
     631             :   {
     632             :     struct TALER_EXCHANGEDB_ReserveOpenListEntry *role;
     633             :     struct TALER_EXCHANGEDB_TransactionList *tl;
     634             :     uint64_t serial_id;
     635             : 
     636           0 :     role = GNUNET_new (struct TALER_EXCHANGEDB_ReserveOpenListEntry);
     637             :     {
     638           0 :       struct GNUNET_PQ_ResultSpec rs[] = {
     639           0 :         GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
     640             :                                               &role->reserve_sig),
     641           0 :         GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
     642             :                                               &role->coin_sig),
     643           0 :         TALER_PQ_RESULT_SPEC_AMOUNT ("contribution",
     644             :                                      &role->coin_contribution),
     645           0 :         GNUNET_PQ_result_spec_uint64 ("reserve_open_deposit_uuid",
     646             :                                       &serial_id),
     647             :         GNUNET_PQ_result_spec_end
     648             :       };
     649             : 
     650           0 :       if (GNUNET_OK !=
     651           0 :           GNUNET_PQ_extract_result (result,
     652             :                                     rs,
     653             :                                     i))
     654             :       {
     655           0 :         GNUNET_break (0);
     656           0 :         GNUNET_free (role);
     657           0 :         chc->failed = true;
     658           0 :         return;
     659             :       }
     660             :     }
     661           0 :     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     662           0 :     tl->next = chc->head;
     663           0 :     tl->type = TALER_EXCHANGEDB_TT_RESERVE_OPEN;
     664           0 :     tl->details.reserve_open = role;
     665           0 :     tl->serial_id = serial_id;
     666           0 :     chc->head = tl;
     667             :   }
     668             : }
     669             : 
     670             : 
     671             : /**
     672             :  * Work we need to do.
     673             :  */
     674             : struct Work
     675             : {
     676             :   /**
     677             :    * Name of the table.
     678             :    */
     679             :   const char *table;
     680             : 
     681             :   /**
     682             :    * SQL prepared statement name.
     683             :    */
     684             :   const char *statement;
     685             : 
     686             :   /**
     687             :    * Function to call to handle the result(s).
     688             :    */
     689             :   GNUNET_PQ_PostgresResultHandler cb;
     690             : };
     691             : 
     692             : 
     693             : /**
     694             :  * We found a coin history entry. Lookup details
     695             :  * from the respective table and store in @a cls.
     696             :  *
     697             :  * @param[in,out] cls a `struct CoinHistoryContext`
     698             :  * @param result a coin history entry result set
     699             :  * @param num_results total number of results in @a results
     700             :  */
     701             : static void
     702         148 : handle_history_entry (void *cls,
     703             :                       PGresult *result,
     704             :                       unsigned int num_results)
     705             : {
     706         148 :   struct CoinHistoryContext *chc = cls;
     707         148 :   struct PostgresClosure *pg = chc->pg;
     708             :   static const struct Work work[] = {
     709             :     [TALER_EXCHANGEDB_TT_DEPOSIT] =
     710             :     { "coin_deposits",
     711             :       "get_deposit_with_coin_pub",
     712             :       &add_coin_deposit },
     713             :     [TALER_EXCHANGEDB_TT_MELT] =
     714             :     { "refresh",
     715             :       "get_refresh_by_coin",
     716             :       &add_coin_melt },
     717             :     [TALER_EXCHANGEDB_TT_PURSE_DEPOSIT] =
     718             :     { "purse_deposits",
     719             :       "get_purse_deposit_by_coin_pub",
     720             :       &add_coin_purse_deposit },
     721             :     [TALER_EXCHANGEDB_TT_PURSE_REFUND] =
     722             :     { "purse_decision",
     723             :       "get_purse_decision_by_coin_pub",
     724             :       &add_coin_purse_decision },
     725             :     [TALER_EXCHANGEDB_TT_REFUND] =
     726             :     { "refunds",
     727             :       "get_refunds_by_coin",
     728             :       &add_coin_refund },
     729             :     [TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP] =
     730             :     { "recoup_refresh::OLD",
     731             :       "recoup_by_old_coin",
     732             :       &add_old_coin_recoup },
     733             :     [TALER_EXCHANGEDB_TT_RECOUP] =
     734             :     { "recoup",
     735             :       "recoup_by_coin",
     736             :       &add_coin_recoup },
     737             :     [TALER_EXCHANGEDB_TT_RECOUP_REFRESH] =
     738             :     { "recoup_refresh::NEW",
     739             :       "recoup_by_refreshed_coin",
     740             :       &add_coin_recoup_refresh },
     741             :     [TALER_EXCHANGEDB_TT_RESERVE_OPEN] =
     742             :     { "reserves_open_deposits",
     743             :       "reserve_open_by_coin",
     744             :       &add_coin_reserve_open },
     745             :     { NULL, NULL, NULL }
     746             :   };
     747             :   char *table_name;
     748             :   uint64_t serial_id;
     749         148 :   struct GNUNET_PQ_ResultSpec rs[] = {
     750         148 :     GNUNET_PQ_result_spec_string ("table_name",
     751             :                                   &table_name),
     752         148 :     GNUNET_PQ_result_spec_uint64 ("serial_id",
     753             :                                   &serial_id),
     754             :     GNUNET_PQ_result_spec_end
     755             :   };
     756         148 :   struct GNUNET_PQ_QueryParam params[] = {
     757         148 :     GNUNET_PQ_query_param_auto_from_type (chc->coin_pub),
     758         148 :     GNUNET_PQ_query_param_uint64 (&serial_id),
     759             :     GNUNET_PQ_query_param_end
     760             :   };
     761             : 
     762         537 :   for (unsigned int i = 0; i<num_results; i++)
     763             :   {
     764             :     enum GNUNET_DB_QueryStatus qs;
     765         389 :     bool found = false;
     766             : 
     767         389 :     if (GNUNET_OK !=
     768         389 :         GNUNET_PQ_extract_result (result,
     769             :                                   rs,
     770             :                                   i))
     771             :     {
     772           0 :       GNUNET_break (0);
     773           0 :       chc->failed = true;
     774           0 :       return;
     775             :     }
     776             : 
     777         389 :     for (unsigned int s = 0;
     778         695 :          NULL != work[s].statement;
     779         306 :          s++)
     780             :     {
     781        1001 :       if (0 != strcmp (table_name,
     782         695 :                        work[s].table))
     783         306 :         continue;
     784         389 :       found = true;
     785         389 :       qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
     786         389 :                                                  work[s].statement,
     787             :                                                  params,
     788         389 :                                                  work[s].cb,
     789             :                                                  chc);
     790         389 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     791             :                   "Coin %s had %d transactions at %llu in table %s\n",
     792             :                   TALER_B2S (chc->coin_pub),
     793             :                   (int) qs,
     794             :                   (unsigned long long) serial_id,
     795             :                   table_name);
     796         389 :       if (0 > qs)
     797           0 :         chc->failed = true;
     798         389 :       break;
     799             :     }
     800         389 :     if (! found)
     801             :     {
     802           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     803             :                   "Coin history includes unsupported table `%s`\n",
     804             :                   table_name);
     805           0 :       chc->failed = true;
     806             :     }
     807         389 :     GNUNET_PQ_cleanup_result (rs);
     808         389 :     if (chc->failed)
     809           0 :       break;
     810             :   }
     811             : }
     812             : 
     813             : 
     814             : enum GNUNET_DB_QueryStatus
     815         148 : TEH_PG_get_coin_transactions (
     816             :   void *cls,
     817             :   bool begin_transaction,
     818             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     819             :   uint64_t start_off,
     820             :   uint64_t etag_in,
     821             :   uint64_t *etag_out,
     822             :   struct TALER_Amount *balance,
     823             :   struct TALER_DenominationHashP *h_denom_pub,
     824             :   struct TALER_EXCHANGEDB_TransactionList **tlp)
     825             : {
     826         148 :   struct PostgresClosure *pg = cls;
     827         148 :   struct GNUNET_PQ_QueryParam params[] = {
     828         148 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
     829             :     GNUNET_PQ_query_param_end
     830             :   };
     831         148 :   struct GNUNET_PQ_QueryParam lparams[] = {
     832         148 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
     833         148 :     GNUNET_PQ_query_param_uint64 (&start_off),
     834             :     GNUNET_PQ_query_param_end
     835             :   };
     836         148 :   struct CoinHistoryContext chc = {
     837             :     .head = NULL,
     838             :     .coin_pub = coin_pub,
     839             :     .pg = pg
     840             :   };
     841             : 
     842         148 :   *tlp = NULL;
     843         148 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     844             :               "Getting transactions for coin %s\n",
     845             :               TALER_B2S (coin_pub));
     846         148 :   PREPARE (pg,
     847             :            "get_coin_history_etag_balance",
     848             :            "SELECT"
     849             :            " ch.coin_history_serial_id"
     850             :            ",kc.remaining"
     851             :            ",denom.denom_pub_hash"
     852             :            " FROM coin_history ch"
     853             :            " JOIN known_coins kc"
     854             :            "   USING (coin_pub)"
     855             :            " JOIN denominations denom"
     856             :            "   USING (denominations_serial)"
     857             :            " WHERE coin_pub=$1"
     858             :            " ORDER BY coin_history_serial_id DESC"
     859             :            " LIMIT 1;");
     860         148 :   PREPARE (pg,
     861             :            "get_coin_history",
     862             :            "SELECT"
     863             :            " table_name"
     864             :            ",serial_id"
     865             :            " FROM coin_history"
     866             :            " WHERE coin_pub=$1"
     867             :            "   AND coin_history_serial_id > $2"
     868             :            " ORDER BY coin_history_serial_id DESC;");
     869         148 :   PREPARE (pg,
     870             :            "get_deposit_with_coin_pub",
     871             :            "SELECT"
     872             :            " cdep.amount_with_fee"
     873             :            ",denoms.fee_deposit"
     874             :            ",denoms.denom_pub_hash"
     875             :            ",kc.age_commitment_hash"
     876             :            ",bdep.wallet_timestamp"
     877             :            ",bdep.refund_deadline"
     878             :            ",bdep.wire_deadline"
     879             :            ",bdep.merchant_pub"
     880             :            ",bdep.h_contract_terms"
     881             :            ",bdep.wallet_data_hash"
     882             :            ",bdep.wire_salt"
     883             :            ",wt.payto_uri"
     884             :            ",cdep.coin_sig"
     885             :            ",cdep.coin_deposit_serial_id"
     886             :            ",bdep.done"
     887             :            " FROM coin_deposits cdep"
     888             :            " JOIN batch_deposits bdep"
     889             :            "   USING (batch_deposit_serial_id)"
     890             :            " JOIN wire_targets wt"
     891             :            "   USING (wire_target_h_payto)"
     892             :            " JOIN known_coins kc"
     893             :            "   ON (kc.coin_pub = cdep.coin_pub)"
     894             :            " JOIN denominations denoms"
     895             :            "   USING (denominations_serial)"
     896             :            " WHERE cdep.coin_pub=$1"
     897             :            "   AND cdep.coin_deposit_serial_id=$2;");
     898         148 :   PREPARE (pg,
     899             :            "get_refresh_by_coin",
     900             :            "SELECT"
     901             :            " rc"
     902             :            ",refresh_seed"
     903             :            ",blinding_seed"
     904             :            ",old_coin_sig"
     905             :            ",amount_with_fee"
     906             :            ",denoms.denom_pub_hash"
     907             :            ",denoms.fee_refresh"
     908             :            ",kc.age_commitment_hash"
     909             :            ",refresh_id"
     910             :            " FROM refresh"
     911             :            " JOIN known_coins kc"
     912             :            "   ON (refresh.old_coin_pub = kc.coin_pub)"
     913             :            " JOIN denominations denoms"
     914             :            "   USING (denominations_serial)"
     915             :            " WHERE old_coin_pub=$1"
     916             :            "   AND refresh_id=$2;");
     917         148 :   PREPARE (pg,
     918             :            "get_purse_deposit_by_coin_pub",
     919             :            "SELECT"
     920             :            " partner_base_url"
     921             :            ",pd.amount_with_fee"
     922             :            ",denoms.fee_deposit"
     923             :            ",pd.purse_pub"
     924             :            ",kc.age_commitment_hash"
     925             :            ",pd.coin_sig"
     926             :            ",pd.purse_deposit_serial_id"
     927             :            ",pdes.refunded"
     928             :            " FROM purse_deposits pd"
     929             :            " LEFT JOIN partners"
     930             :            "   USING (partner_serial_id)"
     931             :            " JOIN purse_requests pr"
     932             :            "   USING (purse_pub)"
     933             :            " LEFT JOIN purse_decision pdes"
     934             :            "   USING (purse_pub)"
     935             :            " JOIN known_coins kc"
     936             :            "   ON (pd.coin_pub = kc.coin_pub)"
     937             :            " JOIN denominations denoms"
     938             :            "   USING (denominations_serial)"
     939             :            " WHERE pd.purse_deposit_serial_id=$2"
     940             :            "   AND pd.coin_pub=$1;");
     941         148 :   PREPARE (pg,
     942             :            "get_purse_decision_by_coin_pub",
     943             :            "SELECT"
     944             :            " pdes.purse_pub"
     945             :            ",pd.amount_with_fee"
     946             :            ",denom.fee_refund"
     947             :            ",pdes.purse_decision_serial_id"
     948             :            " FROM purse_decision pdes"
     949             :            " JOIN purse_deposits pd"
     950             :            "   USING (purse_pub)"
     951             :            " JOIN known_coins kc"
     952             :            "   ON (pd.coin_pub = kc.coin_pub)"
     953             :            " JOIN denominations denom"
     954             :            "   USING (denominations_serial)"
     955             :            " WHERE pd.coin_pub=$1"
     956             :            "   AND pdes.purse_decision_serial_id=$2"
     957             :            "   AND pdes.refunded;");
     958         148 :   PREPARE (pg,
     959             :            "get_refunds_by_coin",
     960             :            "SELECT"
     961             :            " bdep.merchant_pub"
     962             :            ",ref.merchant_sig"
     963             :            ",bdep.h_contract_terms"
     964             :            ",ref.rtransaction_id"
     965             :            ",ref.amount_with_fee"
     966             :            ",denom.fee_refund"
     967             :            ",ref.refund_serial_id"
     968             :            " FROM refunds ref"
     969             :            " JOIN coin_deposits cdep"
     970             :            "   ON (ref.coin_pub = cdep.coin_pub AND ref.batch_deposit_serial_id = cdep.batch_deposit_serial_id)"
     971             :            " JOIN batch_deposits bdep"
     972             :            "   ON (ref.batch_deposit_serial_id = bdep.batch_deposit_serial_id)"
     973             :            " JOIN known_coins kc"
     974             :            "   ON (ref.coin_pub = kc.coin_pub)"
     975             :            " JOIN denominations denom"
     976             :            "   USING (denominations_serial)"
     977             :            " WHERE ref.coin_pub=$1"
     978             :            "   AND ref.refund_serial_id=$2;");
     979         148 :   PREPARE (pg,
     980             :            "recoup_by_old_coin",
     981             :            "SELECT"
     982             :            " coins.coin_pub"
     983             :            ",rr.coin_sig"
     984             :            ",rr.coin_blind"
     985             :            ",rr.amount"
     986             :            ",rr.recoup_timestamp"
     987             :            ",denoms.denom_pub_hash"
     988             :            ",coins.denom_sig"
     989             :            ",rr.recoup_refresh_uuid"
     990             :            " FROM recoup_refresh rr"
     991             :            " JOIN known_coins coins"
     992             :            "   USING (coin_pub)"
     993             :            " JOIN denominations denoms"
     994             :            "   USING (denominations_serial)"
     995             :            " WHERE recoup_refresh_uuid=$2"
     996             :            "   AND refresh_id IN"
     997             :            "   (SELECT refresh_id"
     998             :            "    FROM refresh"
     999             :            "    WHERE refresh.old_coin_pub=$1);");
    1000         148 :   PREPARE (pg,
    1001             :            "recoup_by_coin",
    1002             :            "SELECT"
    1003             :            " res.reserve_pub"
    1004             :            ",denoms.denom_pub_hash"
    1005             :            ",rcp.coin_sig"
    1006             :            ",rcp.coin_blind"
    1007             :            ",rcp.amount"
    1008             :            ",rcp.recoup_timestamp"
    1009             :            ",rcp.recoup_uuid"
    1010             :            " FROM recoup rcp"
    1011             :            " JOIN withdraw ro"
    1012             :            "   USING (withdraw_id)"
    1013             :            " JOIN reserves res"
    1014             :            "   USING (reserve_pub)"
    1015             :            " JOIN known_coins coins"
    1016             :            "   USING (coin_pub)"
    1017             :            " JOIN denominations denoms"
    1018             :            "   ON (denoms.denominations_serial = coins.denominations_serial)"
    1019             :            " WHERE rcp.recoup_uuid=$2"
    1020             :            "   AND coins.coin_pub=$1;");
    1021             :   /* Used to obtain recoup transactions
    1022             :      for a refreshed coin */
    1023         148 :   PREPARE (pg,
    1024             :            "recoup_by_refreshed_coin",
    1025             :            "SELECT"
    1026             :            " old_coins.coin_pub AS old_coin_pub"
    1027             :            ",rr.coin_sig"
    1028             :            ",rr.coin_blind"
    1029             :            ",rr.amount"
    1030             :            ",rr.recoup_timestamp"
    1031             :            ",denoms.denom_pub_hash"
    1032             :            ",coins.denom_sig"
    1033             :            ",recoup_refresh_uuid"
    1034             :            " FROM recoup_refresh rr"
    1035             :            "    JOIN refresh rfc"
    1036             :            "      ON (rr.refresh_id = rfc.refresh_id)"
    1037             :            "    JOIN known_coins old_coins"
    1038             :            "      ON (rfc.old_coin_pub = old_coins.coin_pub)"
    1039             :            "    JOIN known_coins coins"
    1040             :            "      ON (rr.coin_pub = coins.coin_pub)"
    1041             :            "    JOIN denominations denoms"
    1042             :            "      ON (denoms.denominations_serial = coins.denominations_serial)"
    1043             :            " WHERE rr.recoup_refresh_uuid=$2"
    1044             :            "   AND coins.coin_pub=$1;");
    1045         148 :   PREPARE (pg,
    1046             :            "reserve_open_by_coin",
    1047             :            "SELECT"
    1048             :            " reserve_open_deposit_uuid"
    1049             :            ",coin_sig"
    1050             :            ",reserve_sig"
    1051             :            ",contribution"
    1052             :            " FROM reserves_open_deposits"
    1053             :            " WHERE coin_pub=$1"
    1054             :            "   AND reserve_open_deposit_uuid=$2;");
    1055         148 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1056             :               "  --- landed here 1\n");
    1057         148 :   for (unsigned int i = 0; i<RETRIES; i++)
    1058             :   {
    1059             :     enum GNUNET_DB_QueryStatus qs;
    1060             :     uint64_t end;
    1061         148 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1062         148 :       GNUNET_PQ_result_spec_uint64 ("coin_history_serial_id",
    1063             :                                     &end),
    1064         148 :       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
    1065             :                                             h_denom_pub),
    1066         148 :       TALER_PQ_RESULT_SPEC_AMOUNT ("remaining",
    1067             :                                    balance),
    1068             :       GNUNET_PQ_result_spec_end
    1069             :     };
    1070             : 
    1071         148 :     if (begin_transaction)
    1072             :     {
    1073           4 :       if (GNUNET_OK !=
    1074           4 :           TEH_PG_start_read_committed (pg,
    1075             :                                        "get-coin-transactions"))
    1076             :       {
    1077           0 :         GNUNET_break (0);
    1078         148 :         return GNUNET_DB_STATUS_HARD_ERROR;
    1079             :       }
    1080             :     }
    1081             :     /* First only check the last item, to see if
    1082             :        we even need to iterate */
    1083         148 :     qs = GNUNET_PQ_eval_prepared_singleton_select (
    1084             :       pg->conn,
    1085             :       "get_coin_history_etag_balance",
    1086             :       params,
    1087             :       rs);
    1088         148 :     switch (qs)
    1089             :     {
    1090           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1091           0 :       if (begin_transaction)
    1092           0 :         TEH_PG_rollback (pg);
    1093           0 :       return qs;
    1094           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1095           0 :       if (begin_transaction)
    1096           0 :         TEH_PG_rollback (pg);
    1097           0 :       continue;
    1098           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1099           0 :       if (begin_transaction)
    1100           0 :         TEH_PG_rollback (pg);
    1101           0 :       return qs;
    1102         148 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1103         148 :       *etag_out = end;
    1104         148 :       if (end == etag_in)
    1105           0 :         return qs;
    1106             :     }
    1107             :     /* We indeed need to iterate over the history */
    1108         148 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1109             :                 "Current ETag for coin %s is %llu\n",
    1110             :                 TALER_B2S (coin_pub),
    1111             :                 (unsigned long long) end);
    1112             : 
    1113         148 :     qs = GNUNET_PQ_eval_prepared_multi_select (
    1114             :       pg->conn,
    1115             :       "get_coin_history",
    1116             :       lparams,
    1117             :       &handle_history_entry,
    1118             :       &chc);
    1119         148 :     switch (qs)
    1120             :     {
    1121           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1122           0 :       if (begin_transaction)
    1123           0 :         TEH_PG_rollback (pg);
    1124           0 :       return qs;
    1125           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1126           0 :       if (begin_transaction)
    1127           0 :         TEH_PG_rollback (pg);
    1128           0 :       continue;
    1129         148 :     default:
    1130         148 :       break;
    1131             :     }
    1132         148 :     if (chc.failed)
    1133             :     {
    1134           0 :       if (begin_transaction)
    1135           0 :         TEH_PG_rollback (pg);
    1136           0 :       TEH_COMMON_free_coin_transaction_list (pg,
    1137             :                                              chc.head);
    1138           0 :       return GNUNET_DB_STATUS_SOFT_ERROR;
    1139             :     }
    1140         148 :     if (! begin_transaction)
    1141             :     {
    1142         144 :       *tlp = chc.head;
    1143         144 :       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1144             :     }
    1145           4 :     qs = TEH_PG_commit (pg);
    1146           4 :     switch (qs)
    1147             :     {
    1148           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1149           0 :       TEH_COMMON_free_coin_transaction_list (pg,
    1150             :                                              chc.head);
    1151           0 :       chc.head = NULL;
    1152           0 :       return qs;
    1153           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1154           0 :       TEH_COMMON_free_coin_transaction_list (pg,
    1155             :                                              chc.head);
    1156           0 :       chc.head = NULL;
    1157           0 :       continue;
    1158           4 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1159             :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1160           4 :       *tlp = chc.head;
    1161           4 :       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1162             :     }
    1163             :   }
    1164           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1165             :               "  --- landed here 2\n");
    1166           0 :   return GNUNET_DB_STATUS_SOFT_ERROR;
    1167             : }

Generated by: LCOV version 1.16