LCOV - code coverage report
Current view: top level - backenddb - plugin_merchantdb_postgres.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 1764 2081 84.8 %
Date: 2021-08-30 06:54:17 Functions: 109 111 98.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2014--2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU Lesser 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 plugin_merchantdb_postgres.c
      18             :  * @brief database helper functions for postgres used by the merchant
      19             :  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
      20             :  * @author Christian Grothoff
      21             :  * @author Marcello Stanisci
      22             :  */
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <gnunet/gnunet_pq_lib.h>
      26             : #include <taler/taler_util.h>
      27             : #include <taler/taler_pq_lib.h>
      28             : #include <taler/taler_json_lib.h>
      29             : #include <taler/taler_mhd_lib.h>
      30             : #include "taler_merchantdb_plugin.h"
      31             : 
      32             : /**
      33             :  * How often do we re-try if we run into a DB serialization error?
      34             :  */
      35             : #define MAX_RETRIES 3
      36             : 
      37             : 
      38             : /**
      39             :  * Wrapper macro to add the currency from the plugin's state
      40             :  * when fetching amounts from the database.
      41             :  *
      42             :  * @param field name of the database field to fetch amount from
      43             :  * @param[out] amountp pointer to amount to set
      44             :  */
      45             : #define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) \
      46             :   TALER_PQ_result_spec_amount (                    \
      47             :     field,pg->currency,amountp)
      48             : 
      49             : /**
      50             :  * Wrapper macro to add the currency from the plugin's state
      51             :  * when fetching amounts from the database.  NBO variant.
      52             :  *
      53             :  * @param field name of the database field to fetch amount from
      54             :  * @param[out] amountp pointer to amount to set
      55             :  */
      56             : #define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, amountp) \
      57             :   TALER_PQ_result_spec_amount_nbo (                     \
      58             :     field,pg->currency,amountp)
      59             : 
      60             : 
      61             : /**
      62             :  * Wrapper macro to add the currency from the plugin's state
      63             :  * when fetching amounts from the database.
      64             :  *
      65             :  * @param field name of the database field to fetch amount from
      66             :  * @param[out] amountp pointer to amount to set
      67             :  */
      68             : #define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) \
      69             :   TALER_PQ_result_spec_amount (                    \
      70             :     field,pg->currency,amountp)
      71             : 
      72             : 
      73             : /**
      74             :  * Type of the "cls" argument given to each of the functions in
      75             :  * our API.
      76             :  */
      77             : struct PostgresClosure
      78             : {
      79             : 
      80             :   /**
      81             :    * Postgres connection handle.
      82             :    */
      83             :   struct GNUNET_PQ_Context *conn;
      84             : 
      85             :   /**
      86             :    * Which currency do we deal in?
      87             :    */
      88             :   char *currency;
      89             : 
      90             :   /**
      91             :    * Directory with SQL statements to run to create tables.
      92             :    */
      93             :   char *sql_dir;
      94             : 
      95             :   /**
      96             :    * Underlying configuration.
      97             :    */
      98             :   const struct GNUNET_CONFIGURATION_Handle *cfg;
      99             : 
     100             :   /**
     101             :    * Name of the currently active transaction, NULL if none is active.
     102             :    */
     103             :   const char *transaction_name;
     104             : 
     105             : };
     106             : 
     107             : 
     108             : /**
     109             :  * Drop all Taler tables.  This should only be used by testcases.
     110             :  *
     111             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     112             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     113             :  */
     114             : static int
     115           4 : postgres_drop_tables (void *cls)
     116             : {
     117           4 :   struct PostgresClosure *pc = cls;
     118             :   struct GNUNET_PQ_Context *conn;
     119             : 
     120           4 :   conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
     121             :                                      "merchantdb-postgres",
     122             :                                      "drop",
     123             :                                      NULL,
     124             :                                      NULL);
     125           4 :   if (NULL == conn)
     126           2 :     return GNUNET_SYSERR;
     127           2 :   GNUNET_PQ_disconnect (conn);
     128           2 :   return GNUNET_OK;
     129             : }
     130             : 
     131             : 
     132             : /**
     133             :  * Initialize tables.
     134             :  *
     135             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     136             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     137             :  */
     138             : static int
     139          17 : postgres_create_tables (void *cls)
     140             : {
     141          17 :   struct PostgresClosure *pc = cls;
     142             :   struct GNUNET_PQ_Context *conn;
     143             : 
     144          17 :   conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
     145             :                                      "merchantdb-postgres",
     146             :                                      "merchant-",
     147             :                                      NULL,
     148             :                                      NULL);
     149          17 :   if (NULL == conn)
     150           0 :     return GNUNET_SYSERR;
     151          17 :   GNUNET_PQ_disconnect (conn);
     152          17 :   return GNUNET_OK;
     153             : }
     154             : 
     155             : 
     156             : /**
     157             :  * Register callback to be invoked on events of type @a es.
     158             :  *
     159             :  * @param cls database context to use
     160             :  * @param es specification of the event to listen for
     161             :  * @param timeout how long to wait for the event
     162             :  * @param cb function to call when the event happens, possibly
     163             :  *         multiple times (until cancel is invoked)
     164             :  * @param cb_cls closure for @a cb
     165             :  * @return handle useful to cancel the listener
     166             :  */
     167             : static struct GNUNET_DB_EventHandler *
     168          27 : postgres_event_listen (void *cls,
     169             :                        const struct GNUNET_DB_EventHeaderP *es,
     170             :                        struct GNUNET_TIME_Relative timeout,
     171             :                        GNUNET_DB_EventCallback cb,
     172             :                        void *cb_cls)
     173             : {
     174          27 :   struct PostgresClosure *pg = cls;
     175             : 
     176          27 :   return GNUNET_PQ_event_listen (pg->conn,
     177             :                                  es,
     178             :                                  timeout,
     179             :                                  cb,
     180             :                                  cb_cls);
     181             : }
     182             : 
     183             : 
     184             : /**
     185             :  * Stop notifications.
     186             :  *
     187             :  * @param eh handle to unregister.
     188             :  */
     189             : static void
     190          27 : postgres_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
     191             : {
     192          27 :   GNUNET_PQ_event_listen_cancel (eh);
     193          27 : }
     194             : 
     195             : 
     196             : /**
     197             :  * Notify all that listen on @a es of an event.
     198             :  *
     199             :  * @param cls database context to use
     200             :  * @param es specification of the event to generate
     201             :  * @param extra additional event data provided
     202             :  * @param extra_size number of bytes in @a extra
     203             :  */
     204             : static void
     205          88 : postgres_event_notify (void *cls,
     206             :                        const struct GNUNET_DB_EventHeaderP *es,
     207             :                        const void *extra,
     208             :                        size_t extra_size)
     209             : {
     210          88 :   struct PostgresClosure *pg = cls;
     211             : 
     212          88 :   return GNUNET_PQ_event_notify (pg->conn,
     213             :                                  es,
     214             :                                  extra,
     215             :                                  extra_size);
     216             : }
     217             : 
     218             : 
     219             : /**
     220             :  * Do a pre-flight check that we are not in an uncommitted transaction.
     221             :  * If we are, die.
     222             :  * Does not return anything, as we will continue regardless of the outcome.
     223             :  *
     224             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     225             :  */
     226             : static void
     227         639 : postgres_preflight (void *cls)
     228             : {
     229         639 :   struct PostgresClosure *pg = cls;
     230             : 
     231         639 :   if (NULL == pg->transaction_name)
     232         639 :     return; /* all good */
     233           0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     234             :               "BUG: Preflight check detected running transaction `%s'!\n",
     235             :               pg->transaction_name);
     236           0 :   GNUNET_assert (0);
     237             : }
     238             : 
     239             : 
     240             : /**
     241             :  * Check that the database connection is still up
     242             :  * and automatically reconnects unless we are
     243             :  * already inside of a transaction.
     244             :  *
     245             :  * @param pg connection to check
     246             :  */
     247             : static void
     248        1405 : check_connection (struct PostgresClosure *pg)
     249             : {
     250        1405 :   if (NULL != pg->transaction_name)
     251         379 :     return;
     252        1026 :   GNUNET_PQ_reconnect_if_down (pg->conn);
     253             : }
     254             : 
     255             : 
     256             : /**
     257             :  * Start a transaction.
     258             :  *
     259             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     260             :  * @param name unique name identifying the transaction (for debugging),
     261             :  *             must point to a constant
     262             :  * @return #GNUNET_OK on success
     263             :  */
     264             : static int
     265         220 : postgres_start (void *cls,
     266             :                 const char *name)
     267             : {
     268         220 :   struct PostgresClosure *pg = cls;
     269         220 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     270         220 :     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
     271             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     272             :   };
     273             : 
     274         220 :   check_connection (pg);
     275         220 :   postgres_preflight (pg);
     276         220 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     277             :               "Starting merchant DB transaction `%s'\n",
     278             :               name);
     279         220 :   if (GNUNET_OK !=
     280         220 :       GNUNET_PQ_exec_statements (pg->conn,
     281             :                                  es))
     282             :   {
     283           0 :     TALER_LOG_ERROR ("Failed to start transaction\n");
     284           0 :     GNUNET_break (0);
     285           0 :     return GNUNET_SYSERR;
     286             :   }
     287         220 :   pg->transaction_name = name;
     288         220 :   return GNUNET_OK;
     289             : }
     290             : 
     291             : 
     292             : /**
     293             :  * Start a transaction in 'read committed' mode.
     294             :  *
     295             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     296             :  * @param name unique name identifying the transaction (for debugging),
     297             :  *             must point to a constant
     298             :  * @return #GNUNET_OK on success
     299             :  */
     300             : static int
     301           2 : postgres_start_read_committed (void *cls,
     302             :                                const char *name)
     303             : {
     304           2 :   struct PostgresClosure *pg = cls;
     305           2 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     306           2 :     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL READ COMMITTED"),
     307             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     308             :   };
     309             : 
     310           2 :   check_connection (pg);
     311           2 :   postgres_preflight (pg);
     312           2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     313             :               "Starting merchant DB transaction %s (READ COMMITTED)\n",
     314             :               name);
     315           2 :   if (GNUNET_OK !=
     316           2 :       GNUNET_PQ_exec_statements (pg->conn,
     317             :                                  es))
     318             :   {
     319           0 :     TALER_LOG_ERROR ("Failed to start transaction\n");
     320           0 :     GNUNET_break (0);
     321           0 :     return GNUNET_SYSERR;
     322             :   }
     323           2 :   pg->transaction_name = name;
     324           2 :   return GNUNET_OK;
     325             : }
     326             : 
     327             : 
     328             : /**
     329             :  * Roll back the current transaction of a database connection.
     330             :  *
     331             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     332             :  */
     333             : static void
     334          45 : postgres_rollback (void *cls)
     335             : {
     336          45 :   struct PostgresClosure *pg = cls;
     337          45 :   struct GNUNET_PQ_ExecuteStatement es[] = {
     338          45 :     GNUNET_PQ_make_execute ("ROLLBACK"),
     339             :     GNUNET_PQ_EXECUTE_STATEMENT_END
     340             :   };
     341             : 
     342          45 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     343             :               "Rolling back merchant DB transaction `%s'\n",
     344             :               pg->transaction_name);
     345          45 :   GNUNET_break (GNUNET_OK ==
     346             :                 GNUNET_PQ_exec_statements (pg->conn,
     347             :                                            es));
     348          45 :   pg->transaction_name = NULL;
     349          45 : }
     350             : 
     351             : 
     352             : /**
     353             :  * Commit the current transaction of a database connection.
     354             :  *
     355             :  * @param cls the `struct PostgresClosure` with the plugin-specific state
     356             :  * @return transaction status code
     357             :  */
     358             : static enum GNUNET_DB_QueryStatus
     359         178 : postgres_commit (void *cls)
     360             : {
     361         178 :   struct PostgresClosure *pg = cls;
     362         178 :   struct GNUNET_PQ_QueryParam params[] = {
     363             :     GNUNET_PQ_query_param_end
     364             :   };
     365             : 
     366         178 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     367             :               "Committing merchant DB transaction %s\n",
     368             :               pg->transaction_name);
     369         178 :   pg->transaction_name = NULL;
     370         178 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     371             :                                              "end_transaction",
     372             :                                              params);
     373             : }
     374             : 
     375             : 
     376             : /**
     377             :  * Context for lookup_instances().
     378             :  */
     379             : struct LookupInstancesContext
     380             : {
     381             :   /**
     382             :    * Function to call with the results.
     383             :    */
     384             :   TALER_MERCHANTDB_InstanceCallback cb;
     385             : 
     386             :   /**
     387             :    * Closure for @e cb.
     388             :    */
     389             :   void *cb_cls;
     390             : 
     391             :   /**
     392             :    * Database context.
     393             :    */
     394             :   struct PostgresClosure *pg;
     395             : 
     396             :   /**
     397             :    * Instance settings, valid only during find_instances_cb().
     398             :    */
     399             :   struct TALER_MERCHANTDB_InstanceSettings is;
     400             : 
     401             :   /**
     402             :    * Instance authentication settings, valid only during find_instances_cb().
     403             :    */
     404             :   struct TALER_MERCHANTDB_InstanceAuthSettings ias;
     405             : 
     406             :   /**
     407             :    * Instance serial number, valid only during find_instances_cb().
     408             :    */
     409             :   uint64_t instance_serial;
     410             : 
     411             :   /**
     412             :    * Public key of the current instance, valid only during find_instances_cb().
     413             :    */
     414             :   struct TALER_MerchantPublicKeyP merchant_pub;
     415             : 
     416             :   /**
     417             :    * Set to the return value on errors.
     418             :    */
     419             :   enum GNUNET_DB_QueryStatus qs;
     420             : 
     421             :   /**
     422             :    * true if we only are interested in instances for which we have the private key.
     423             :    */
     424             :   bool active_only;
     425             : };
     426             : 
     427             : 
     428             : /**
     429             :  * We are processing an instances lookup and have the @a accounts.
     430             :  * Find the private key if possible, and invoke the callback.
     431             :  *
     432             :  * @param lic context we are handling
     433             :  * @param num_accounts length of @a accounts array
     434             :  * @param accounts information about accounts of the instance in @a lic
     435             :  */
     436             : static void
     437          47 : call_with_accounts (struct LookupInstancesContext *lic,
     438             :                     unsigned int num_accounts,
     439             :                     const struct TALER_MERCHANTDB_AccountDetails accounts[])
     440             : {
     441          47 :   struct PostgresClosure *pg = lic->pg;
     442             :   enum GNUNET_DB_QueryStatus qs;
     443          47 :   struct GNUNET_PQ_QueryParam params[] = {
     444          47 :     GNUNET_PQ_query_param_uint64 (&lic->instance_serial),
     445             :     GNUNET_PQ_query_param_end
     446             :   };
     447             :   struct TALER_MerchantPrivateKeyP merchant_priv;
     448          47 :   struct GNUNET_PQ_ResultSpec rs[] = {
     449          47 :     GNUNET_PQ_result_spec_auto_from_type ("merchant_priv",
     450             :                                           &merchant_priv),
     451             :     GNUNET_PQ_result_spec_end
     452             :   };
     453             : 
     454          47 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
     455             :                                                  "lookup_instance_private_key",
     456             :                                                  params,
     457             :                                                  rs);
     458          47 :   if (qs < 0)
     459             :   {
     460           0 :     GNUNET_break (0);
     461           0 :     lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
     462           2 :     return;
     463             :   }
     464          47 :   if ( (0 == qs) &&
     465           6 :        (lic->active_only) )
     466           2 :     return; /* skip, not interesting */
     467          45 :   lic->cb (lic->cb_cls,
     468          45 :            &lic->merchant_pub,
     469             :            (0 == qs) ? NULL : &merchant_priv,
     470          45 :            &lic->is,
     471          45 :            &lic->ias,
     472             :            num_accounts,
     473             :            accounts);
     474             : }
     475             : 
     476             : 
     477             : /**
     478             :  * Function to be called with the results of a SELECT statement
     479             :  * that has returned @a num_results results about accounts.
     480             :  *
     481             :  * @param cls of type `struct FindInstancesContext *`
     482             :  * @param result the postgres result
     483             :  * @param num_results the number of results in @a result
     484             :  */
     485             : static void
     486          47 : lookup_accounts_cb (void *cls,
     487             :                     PGresult *result,
     488             :                     unsigned int num_results)
     489          47 : {
     490          47 :   struct LookupInstancesContext *lic = cls;
     491          47 :   char *paytos[num_results];
     492          47 :   struct TALER_MERCHANTDB_AccountDetails accounts[num_results];
     493             : 
     494          91 :   for (unsigned int i = 0; i < num_results; i++)
     495             :   {
     496             :     uint8_t active;
     497          44 :     struct GNUNET_PQ_ResultSpec rs[] = {
     498          44 :       GNUNET_PQ_result_spec_auto_from_type ("h_wire",
     499             :                                             &accounts[i].h_wire),
     500          44 :       GNUNET_PQ_result_spec_auto_from_type ("salt",
     501             :                                             &accounts[i].salt),
     502          44 :       GNUNET_PQ_result_spec_string ("payto_uri",
     503             :                                     &paytos[i]),
     504          44 :       GNUNET_PQ_result_spec_auto_from_type ("active",
     505             :                                             &active),
     506             :       GNUNET_PQ_result_spec_end
     507             :     };
     508             : 
     509          44 :     if (GNUNET_OK !=
     510          44 :         GNUNET_PQ_extract_result (result,
     511             :                                   rs,
     512             :                                   i))
     513             :     {
     514           0 :       GNUNET_break (0);
     515           0 :       lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
     516           0 :       for (unsigned int j = 0; j < i; j++)
     517           0 :         GNUNET_free (paytos[j]);
     518           0 :       return;
     519             :     }
     520          44 :     accounts[i].active = (0 != active);
     521          44 :     accounts[i].payto_uri = paytos[i];
     522             :   }
     523          47 :   call_with_accounts (lic,
     524             :                       num_results,
     525             :                       accounts);
     526          91 :   for (unsigned int i = 0; i < num_results; i++)
     527          44 :     GNUNET_free (paytos[i]);
     528             : }
     529             : 
     530             : 
     531             : /**
     532             :  * Function to be called with the results of a SELECT statement
     533             :  * that has returned @a num_results results about instances.
     534             :  *
     535             :  * @param cls of type `struct FindInstancesContext *`
     536             :  * @param result the postgres result
     537             :  * @param num_results the number of results in @a result
     538             :  */
     539             : static void
     540          67 : lookup_instances_cb (void *cls,
     541             :                      PGresult *result,
     542             :                      unsigned int num_results)
     543             : {
     544          67 :   struct LookupInstancesContext *lic = cls;
     545          67 :   struct PostgresClosure *pg = lic->pg;
     546             : 
     547         114 :   for (unsigned int i = 0; i < num_results; i++)
     548             :   {
     549             :     bool no_auth;
     550             :     bool no_salt;
     551          47 :     struct GNUNET_PQ_ResultSpec rs[] = {
     552          47 :       GNUNET_PQ_result_spec_uint64 ("merchant_serial",
     553             :                                     &lic->instance_serial),
     554          47 :       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
     555             :                                             &lic->merchant_pub),
     556          47 :       GNUNET_PQ_result_spec_allow_null (
     557          47 :         GNUNET_PQ_result_spec_auto_from_type ("auth_hash",
     558             :                                               &lic->ias.auth_hash),
     559             :         &no_auth),
     560          47 :       GNUNET_PQ_result_spec_allow_null (
     561          47 :         GNUNET_PQ_result_spec_auto_from_type ("auth_salt",
     562             :                                               &lic->ias.auth_salt),
     563             :         &no_salt),
     564          47 :       GNUNET_PQ_result_spec_string ("merchant_id",
     565             :                                     &lic->is.id),
     566          47 :       GNUNET_PQ_result_spec_string ("merchant_name",
     567             :                                     &lic->is.name),
     568          47 :       TALER_PQ_result_spec_json ("address",
     569             :                                  &lic->is.address),
     570          47 :       TALER_PQ_result_spec_json ("jurisdiction",
     571             :                                  &lic->is.jurisdiction),
     572          47 :       TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_deposit_fee",
     573             :                                    &lic->is.default_max_deposit_fee),
     574          47 :       TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_wire_fee",
     575             :                                    &lic->is.default_max_wire_fee),
     576          47 :       GNUNET_PQ_result_spec_uint32 ("default_wire_fee_amortization",
     577             :                                     &lic->is.default_wire_fee_amortization),
     578          47 :       GNUNET_PQ_result_spec_relative_time ("default_wire_transfer_delay",
     579             :                                            &lic->is.default_wire_transfer_delay),
     580          47 :       GNUNET_PQ_result_spec_relative_time ("default_pay_delay",
     581             :                                            &lic->is.default_pay_delay),
     582             :       GNUNET_PQ_result_spec_end
     583             :     };
     584          47 :     struct GNUNET_PQ_QueryParam params[] = {
     585          47 :       GNUNET_PQ_query_param_uint64 (&lic->instance_serial),
     586             :       GNUNET_PQ_query_param_end
     587             :     };
     588             : 
     589          47 :     memset (&lic->ias.auth_salt,
     590             :             0,
     591             :             sizeof (lic->ias.auth_salt));
     592          47 :     memset (&lic->ias.auth_hash,
     593             :             0,
     594             :             sizeof (lic->ias.auth_hash));
     595          47 :     if (GNUNET_OK !=
     596          47 :         GNUNET_PQ_extract_result (result,
     597             :                                   rs,
     598             :                                   i))
     599             :     {
     600           0 :       GNUNET_break (0);
     601           0 :       lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
     602           0 :       return;
     603             :     }
     604          47 :     lic->qs = GNUNET_PQ_eval_prepared_multi_select (lic->pg->conn,
     605             :                                                     "lookup_accounts",
     606             :                                                     params,
     607             :                                                     &lookup_accounts_cb,
     608             :                                                     lic);
     609          47 :     if (0 > lic->qs)
     610             :     {
     611             :       /* lookup_accounts_cb() did not run, still notify about the
     612             :          account-less instance! */
     613           0 :       call_with_accounts (lic,
     614             :                           0,
     615             :                           NULL);
     616             :     }
     617          47 :     GNUNET_PQ_cleanup_result (rs);
     618          47 :     if (0 > lic->qs)
     619           0 :       break;
     620             :   }
     621             : }
     622             : 
     623             : 
     624             : /**
     625             :  * Lookup all of the instances this backend has configured.
     626             :  *
     627             :  * @param cls closure
     628             :  * @param active_only only find 'active' instances
     629             :  * @param cb function to call on all instances found
     630             :  * @param cb_cls closure for @a cb
     631             :  */
     632             : static enum GNUNET_DB_QueryStatus
     633          25 : postgres_lookup_instances (void *cls,
     634             :                            bool active_only,
     635             :                            TALER_MERCHANTDB_InstanceCallback cb,
     636             :                            void *cb_cls)
     637             : {
     638          25 :   struct PostgresClosure *pg = cls;
     639          25 :   struct LookupInstancesContext lic = {
     640             :     .cb = cb,
     641             :     .cb_cls = cb_cls,
     642             :     .active_only = active_only,
     643             :     .pg = pg
     644             :   };
     645          25 :   struct GNUNET_PQ_QueryParam params[] = {
     646             :     GNUNET_PQ_query_param_end
     647             :   };
     648             :   enum GNUNET_DB_QueryStatus qs;
     649             : 
     650          25 :   check_connection (pg);
     651          25 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
     652             :                                              "lookup_instances",
     653             :                                              params,
     654             :                                              &lookup_instances_cb,
     655             :                                              &lic);
     656          25 :   if (0 > lic.qs)
     657           0 :     return lic.qs;
     658          25 :   return qs;
     659             : }
     660             : 
     661             : 
     662             : /**
     663             :  * Lookup all one of the instances this backend has configured.
     664             :  *
     665             :  * @param cls closure
     666             :  * @param id instance ID to resolve
     667             :  * @param active_only only find 'active' instances
     668             :  * @param cb function to call on all instances found
     669             :  * @param cb_cls closure for @a cb
     670             :  */
     671             : static enum GNUNET_DB_QueryStatus
     672          42 : postgres_lookup_instance (void *cls,
     673             :                           const char *id,
     674             :                           bool active_only,
     675             :                           TALER_MERCHANTDB_InstanceCallback cb,
     676             :                           void *cb_cls)
     677             : {
     678          42 :   struct PostgresClosure *pg = cls;
     679          42 :   struct LookupInstancesContext lic = {
     680             :     .cb = cb,
     681             :     .cb_cls = cb_cls,
     682             :     .active_only = active_only,
     683             :     .pg = pg
     684             :   };
     685          42 :   struct GNUNET_PQ_QueryParam params[] = {
     686          42 :     GNUNET_PQ_query_param_string (id),
     687             :     GNUNET_PQ_query_param_end
     688             :   };
     689             :   enum GNUNET_DB_QueryStatus qs;
     690             : 
     691          42 :   check_connection (pg);
     692          42 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
     693             :                                              "lookup_instance",
     694             :                                              params,
     695             :                                              &lookup_instances_cb,
     696             :                                              &lic);
     697          42 :   if (0 > lic.qs)
     698           0 :     return lic.qs;
     699          42 :   return qs;
     700             : }
     701             : 
     702             : 
     703             : /**
     704             :  * Lookup authentication data of an instance.
     705             :  *
     706             :  * @param cls closure
     707             :  * @param instance_id instance to query
     708             :  * @param[out] ias where to store the auth data
     709             :  */
     710             : static enum GNUNET_DB_QueryStatus
     711           6 : postgres_lookup_instance_auth (
     712             :   void *cls,
     713             :   const char *instance_id,
     714             :   struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
     715             : {
     716           6 :   struct PostgresClosure *pg = cls;
     717           6 :   struct GNUNET_PQ_QueryParam params[] = {
     718           6 :     GNUNET_PQ_query_param_string (instance_id),
     719             :     GNUNET_PQ_query_param_end
     720             :   };
     721           6 :   struct GNUNET_PQ_ResultSpec rs[] = {
     722           6 :     GNUNET_PQ_result_spec_auto_from_type ("auth_hash",
     723             :                                           &ias->auth_hash),
     724           6 :     GNUNET_PQ_result_spec_auto_from_type ("auth_salt",
     725             :                                           &ias->auth_salt),
     726             :     GNUNET_PQ_result_spec_end
     727             :   };
     728             : 
     729           6 :   check_connection (pg);
     730           6 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
     731             :                                                    "lookup_instance_auth",
     732             :                                                    params,
     733             :                                                    rs);
     734             : }
     735             : 
     736             : 
     737             : /**
     738             :  * Insert information about an instance into our database.
     739             :  *
     740             :  * @param cls closure
     741             :  * @param merchant_pub public key of the instance
     742             :  * @param merchant_priv private key of the instance
     743             :  * @param is details about the instance
     744             :  * @param ias authentication settings for the instance
     745             :  * @return database result code
     746             :  */
     747             : static enum GNUNET_DB_QueryStatus
     748          36 : postgres_insert_instance (
     749             :   void *cls,
     750             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
     751             :   const struct TALER_MerchantPrivateKeyP *merchant_priv,
     752             :   const struct TALER_MERCHANTDB_InstanceSettings *is,
     753             :   const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
     754             : {
     755          36 :   struct PostgresClosure *pg = cls;
     756          36 :   struct GNUNET_PQ_QueryParam params[] = {
     757          36 :     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
     758          36 :     GNUNET_PQ_query_param_auto_from_type (&ias->auth_hash),
     759          36 :     GNUNET_PQ_query_param_auto_from_type (&ias->auth_salt),
     760          36 :     GNUNET_PQ_query_param_string (is->id),
     761          36 :     GNUNET_PQ_query_param_string (is->name),
     762          36 :     TALER_PQ_query_param_json (is->address),
     763          36 :     TALER_PQ_query_param_json (is->jurisdiction),
     764          36 :     TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
     765          36 :     TALER_PQ_query_param_amount (&is->default_max_wire_fee),
     766          36 :     GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
     767          36 :     GNUNET_PQ_query_param_relative_time (
     768             :       &is->default_wire_transfer_delay),
     769          36 :     GNUNET_PQ_query_param_relative_time (&is->default_pay_delay),
     770             :     GNUNET_PQ_query_param_end
     771             :   };
     772          36 :   struct GNUNET_PQ_QueryParam params_priv[] = {
     773          36 :     GNUNET_PQ_query_param_auto_from_type (merchant_priv),
     774          36 :     GNUNET_PQ_query_param_string (is->id),
     775             :     GNUNET_PQ_query_param_end
     776             :   };
     777             :   enum GNUNET_DB_QueryStatus qs;
     778             : 
     779          36 :   check_connection (pg);
     780          36 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
     781             :                                            "insert_instance",
     782             :                                            params);
     783          36 :   if (qs <= 0)
     784           1 :     return qs;
     785          35 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     786             :                                              "insert_keys",
     787             :                                              params_priv);
     788             : }
     789             : 
     790             : 
     791             : /**
     792             :  * Insert information about an instance's account into our database.
     793             :  *
     794             :  * @param cls closure
     795             :  * @param id identifier of the instance
     796             :  * @param account_details details about the account
     797             :  * @return database result code
     798             :  */
     799             : static enum GNUNET_DB_QueryStatus
     800          34 : postgres_insert_account (
     801             :   void *cls,
     802             :   const char *id,
     803             :   const struct TALER_MERCHANTDB_AccountDetails *account_details)
     804             : {
     805          34 :   struct PostgresClosure *pg = cls;
     806          34 :   uint8_t active = account_details->active;
     807          34 :   struct GNUNET_PQ_QueryParam params[] = {
     808          34 :     GNUNET_PQ_query_param_string (id),
     809          34 :     GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire),
     810          34 :     GNUNET_PQ_query_param_auto_from_type (&account_details->salt),
     811          34 :     GNUNET_PQ_query_param_string (account_details->payto_uri),
     812          34 :     GNUNET_PQ_query_param_auto_from_type (&active),
     813             :     GNUNET_PQ_query_param_end
     814             :   };
     815             : 
     816          34 :   check_connection (pg);
     817          34 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     818             :                                              "insert_account",
     819             :                                              params);
     820             : }
     821             : 
     822             : 
     823             : /**
     824             :  * Delete private key of an instance from our database.
     825             :  *
     826             :  * @param cls closure
     827             :  * @param merchant_id identifier of the instance
     828             :  * @return database result code
     829             :  */
     830             : static enum GNUNET_DB_QueryStatus
     831           3 : postgres_delete_instance_private_key (
     832             :   void *cls,
     833             :   const char *merchant_id)
     834             : {
     835           3 :   struct PostgresClosure *pg = cls;
     836           3 :   struct GNUNET_PQ_QueryParam params[] = {
     837           3 :     GNUNET_PQ_query_param_string (merchant_id),
     838             :     GNUNET_PQ_query_param_end
     839             :   };
     840             : 
     841           3 :   check_connection (pg);
     842           3 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     843             :                                              "delete_key",
     844             :                                              params);
     845             : }
     846             : 
     847             : 
     848             : /**
     849             :  * Purge an instance and all associated information from our database.
     850             :  * Highly likely to cause undesired data loss. Use with caution.
     851             :  *
     852             :  * @param cls closure
     853             :  * @param merchant_id identifier of the instance
     854             :  * @return database result code
     855             :  */
     856             : static enum GNUNET_DB_QueryStatus
     857           8 : postgres_purge_instance (void *cls,
     858             :                          const char *merchant_id)
     859             : {
     860           8 :   struct PostgresClosure *pg = cls;
     861           8 :   struct GNUNET_PQ_QueryParam params[] = {
     862           8 :     GNUNET_PQ_query_param_string (merchant_id),
     863             :     GNUNET_PQ_query_param_end
     864             :   };
     865             : 
     866           8 :   check_connection (pg);
     867           8 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     868             :                                              "purge_instance",
     869             :                                              params);
     870             : }
     871             : 
     872             : 
     873             : /**
     874             :  * Update information about an instance into our database.
     875             :  *
     876             :  * @param cls closure
     877             :  * @param is details about the instance
     878             :  * @return database result code
     879             :  */
     880             : static enum GNUNET_DB_QueryStatus
     881           4 : postgres_update_instance (void *cls,
     882             :                           const struct TALER_MERCHANTDB_InstanceSettings *is)
     883             : {
     884           4 :   struct PostgresClosure *pg = cls;
     885           4 :   struct GNUNET_PQ_QueryParam params[] = {
     886           4 :     GNUNET_PQ_query_param_string (is->id),
     887           4 :     GNUNET_PQ_query_param_string (is->name),
     888           4 :     TALER_PQ_query_param_json (is->address),
     889           4 :     TALER_PQ_query_param_json (is->jurisdiction),
     890           4 :     TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
     891           4 :     TALER_PQ_query_param_amount (&is->default_max_wire_fee),
     892           4 :     GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
     893           4 :     GNUNET_PQ_query_param_relative_time (
     894             :       &is->default_wire_transfer_delay),
     895           4 :     GNUNET_PQ_query_param_relative_time (&is->default_pay_delay),
     896             :     GNUNET_PQ_query_param_end
     897             :   };
     898             : 
     899           4 :   check_connection (pg);
     900           4 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     901             :                                              "update_instance",
     902             :                                              params);
     903             : }
     904             : 
     905             : 
     906             : /**
     907             :  * Update information about an instance's authentication settings
     908             :  * into our database.
     909             :  *
     910             :  * @param cls closure
     911             :  * @param merchant_id identity of the instance
     912             :  * @param is authentication details about the instance
     913             :  * @return database result code
     914             :  */
     915             : static enum GNUNET_DB_QueryStatus
     916           6 : postgres_update_instance_auth (
     917             :   void *cls,
     918             :   const char *merchant_id,
     919             :   const struct TALER_MERCHANTDB_InstanceAuthSettings *is)
     920             : {
     921           6 :   struct PostgresClosure *pg = cls;
     922           6 :   struct GNUNET_PQ_QueryParam params[] = {
     923           6 :     GNUNET_PQ_query_param_string (merchant_id),
     924           6 :     GNUNET_PQ_query_param_auto_from_type (&is->auth_hash),
     925           6 :     GNUNET_PQ_query_param_auto_from_type (&is->auth_salt),
     926             :     GNUNET_PQ_query_param_end
     927             :   };
     928             : 
     929           6 :   check_connection (pg);
     930           6 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     931             :                                              "update_instance_auth",
     932             :                                              params);
     933             : }
     934             : 
     935             : 
     936             : /**
     937             :  * Set an instance's account in our database to "inactive".
     938             :  *
     939             :  * @param cls closure
     940             :  * @param merchant_id merchant backend instance ID
     941             :  * @param h_wire hash of the wire account to set to inactive
     942             :  * @return database result code
     943             :  */
     944             : static enum GNUNET_DB_QueryStatus
     945           3 : postgres_inactivate_account (void *cls,
     946             :                              const char *merchant_id,
     947             :                              const struct GNUNET_HashCode *h_wire)
     948             : {
     949           3 :   struct PostgresClosure *pg = cls;
     950           3 :   struct GNUNET_PQ_QueryParam params[] = {
     951           3 :     GNUNET_PQ_query_param_string (merchant_id),
     952           3 :     GNUNET_PQ_query_param_auto_from_type (h_wire),
     953             :     GNUNET_PQ_query_param_end
     954             :   };
     955             : 
     956           3 :   check_connection (pg);
     957           3 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     958             :                                              "inactivate_account",
     959             :                                              params);
     960             : }
     961             : 
     962             : 
     963             : /**
     964             :  * Set an instance's account in our database to "active".
     965             :  *
     966             :  * @param cls closure
     967             :  * @param merchant_id merchant backend instance ID
     968             :  * @param h_wire hash of the wire account to set to active
     969             :  * @return database result code
     970             :  */
     971             : static enum GNUNET_DB_QueryStatus
     972           0 : postgres_activate_account (void *cls,
     973             :                            const char *merchant_id,
     974             :                            const struct GNUNET_HashCode *h_wire)
     975             : {
     976           0 :   struct PostgresClosure *pg = cls;
     977           0 :   struct GNUNET_PQ_QueryParam params[] = {
     978           0 :     GNUNET_PQ_query_param_string (merchant_id),
     979           0 :     GNUNET_PQ_query_param_auto_from_type (h_wire),
     980             :     GNUNET_PQ_query_param_end
     981             :   };
     982             : 
     983           0 :   check_connection (pg);
     984           0 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
     985             :                                              "activate_account",
     986             :                                              params);
     987             : }
     988             : 
     989             : 
     990             : /**
     991             :  * Context used for postgres_lookup_products().
     992             :  */
     993             : struct LookupProductsContext
     994             : {
     995             :   /**
     996             :    * Function to call with the results.
     997             :    */
     998             :   TALER_MERCHANTDB_ProductsCallback cb;
     999             : 
    1000             :   /**
    1001             :    * Closure for @a cb.
    1002             :    */
    1003             :   void *cb_cls;
    1004             : 
    1005             :   /**
    1006             :    * Did database result extraction fail?
    1007             :    */
    1008             :   bool extract_failed;
    1009             : };
    1010             : 
    1011             : 
    1012             : /**
    1013             :  * Function to be called with the results of a SELECT statement
    1014             :  * that has returned @a num_results results about products.
    1015             :  *
    1016             :  * @param[in,out] cls of type `struct LookupProductsContext *`
    1017             :  * @param result the postgres result
    1018             :  * @param num_results the number of results in @a result
    1019             :  */
    1020             : static void
    1021           8 : lookup_products_cb (void *cls,
    1022             :                     PGresult *result,
    1023             :                     unsigned int num_results)
    1024             : {
    1025           8 :   struct LookupProductsContext *plc = cls;
    1026             : 
    1027          12 :   for (unsigned int i = 0; i < num_results; i++)
    1028             :   {
    1029             :     char *product_id;
    1030           4 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1031           4 :       GNUNET_PQ_result_spec_string ("product_id",
    1032             :                                     &product_id),
    1033             :       GNUNET_PQ_result_spec_end
    1034             :     };
    1035             : 
    1036           4 :     if (GNUNET_OK !=
    1037           4 :         GNUNET_PQ_extract_result (result,
    1038             :                                   rs,
    1039             :                                   i))
    1040             :     {
    1041           0 :       GNUNET_break (0);
    1042           0 :       plc->extract_failed = true;
    1043           0 :       return;
    1044             :     }
    1045           4 :     plc->cb (plc->cb_cls,
    1046             :              product_id);
    1047           4 :     GNUNET_PQ_cleanup_result (rs);
    1048             :   }
    1049             : }
    1050             : 
    1051             : 
    1052             : /**
    1053             :  * Lookup all of the products the given instance has configured.
    1054             :  *
    1055             :  * @param cls closure
    1056             :  * @param instance_id instance to lookup products for
    1057             :  * @param cb function to call on all products found
    1058             :  * @param cb_cls closure for @a cb
    1059             :  * @return database result code
    1060             :  */
    1061             : static enum GNUNET_DB_QueryStatus
    1062           8 : postgres_lookup_products (void *cls,
    1063             :                           const char *instance_id,
    1064             :                           TALER_MERCHANTDB_ProductsCallback cb,
    1065             :                           void *cb_cls)
    1066             : {
    1067           8 :   struct PostgresClosure *pg = cls;
    1068           8 :   struct LookupProductsContext plc = {
    1069             :     .cb = cb,
    1070             :     .cb_cls = cb_cls,
    1071             :     /* Can be overwritten by the lookup_products_cb */
    1072             :     .extract_failed = false,
    1073             :   };
    1074           8 :   struct GNUNET_PQ_QueryParam params[] = {
    1075           8 :     GNUNET_PQ_query_param_string (instance_id),
    1076             :     GNUNET_PQ_query_param_end
    1077             :   };
    1078             :   enum GNUNET_DB_QueryStatus qs;
    1079             : 
    1080           8 :   check_connection (pg);
    1081           8 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    1082             :                                              "lookup_products",
    1083             :                                              params,
    1084             :                                              &lookup_products_cb,
    1085             :                                              &plc);
    1086             :   /* If there was an error inside lookup_products_cb, return a hard error. */
    1087           8 :   if (plc.extract_failed)
    1088           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1089           8 :   return qs;
    1090             : }
    1091             : 
    1092             : 
    1093             : /**
    1094             :  * Lookup details about a particular product.
    1095             :  *
    1096             :  * @param cls closure
    1097             :  * @param instance_id instance to lookup products for
    1098             :  * @param product_id product to lookup
    1099             :  * @param[out] pd set to the product details on success, can be NULL
    1100             :  *             (in that case we only want to check if the product exists)
    1101             :  * @return database result code
    1102             :  */
    1103             : static enum GNUNET_DB_QueryStatus
    1104          24 : postgres_lookup_product (void *cls,
    1105             :                          const char *instance_id,
    1106             :                          const char *product_id,
    1107             :                          struct TALER_MERCHANTDB_ProductDetails *pd)
    1108             : {
    1109          24 :   struct PostgresClosure *pg = cls;
    1110          24 :   struct GNUNET_PQ_QueryParam params[] = {
    1111          24 :     GNUNET_PQ_query_param_string (instance_id),
    1112          24 :     GNUNET_PQ_query_param_string (product_id),
    1113             :     GNUNET_PQ_query_param_end
    1114             :   };
    1115             : 
    1116          24 :   if (NULL == pd)
    1117             :   {
    1118           5 :     struct GNUNET_PQ_ResultSpec rs_null[] = {
    1119             :       GNUNET_PQ_result_spec_end
    1120             :     };
    1121             : 
    1122           5 :     check_connection (pg);
    1123           5 :     return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    1124             :                                                      "lookup_product",
    1125             :                                                      params,
    1126             :                                                      rs_null);
    1127             :   }
    1128             :   else
    1129             :   {
    1130          19 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1131          19 :       GNUNET_PQ_result_spec_string ("description",
    1132             :                                     &pd->description),
    1133          19 :       TALER_PQ_result_spec_json ("description_i18n",
    1134             :                                  &pd->description_i18n),
    1135          19 :       GNUNET_PQ_result_spec_string ("unit",
    1136             :                                     &pd->unit),
    1137          19 :       TALER_PQ_RESULT_SPEC_AMOUNT ("price",
    1138             :                                    &pd->price),
    1139          19 :       TALER_PQ_result_spec_json ("taxes",
    1140             :                                  &pd->taxes),
    1141          19 :       GNUNET_PQ_result_spec_uint64 ("total_stock",
    1142             :                                     &pd->total_stock),
    1143          19 :       GNUNET_PQ_result_spec_uint64 ("total_sold",
    1144             :                                     &pd->total_sold),
    1145          19 :       GNUNET_PQ_result_spec_uint64 ("total_lost",
    1146             :                                     &pd->total_lost),
    1147          19 :       GNUNET_PQ_result_spec_string ("image",
    1148             :                                     &pd->image),
    1149          19 :       TALER_PQ_result_spec_json ("address",
    1150             :                                  &pd->address),
    1151          19 :       GNUNET_PQ_result_spec_absolute_time ("next_restock",
    1152             :                                            &pd->next_restock),
    1153             :       GNUNET_PQ_result_spec_end
    1154             :     };
    1155             : 
    1156          19 :     check_connection (pg);
    1157          19 :     return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    1158             :                                                      "lookup_product",
    1159             :                                                      params,
    1160             :                                                      rs);
    1161             :   }
    1162             : }
    1163             : 
    1164             : 
    1165             : /**
    1166             :  * Delete information about a product.  Note that the transaction must
    1167             :  * enforce that no stocks are currently locked.
    1168             :  *
    1169             :  * @param cls closure
    1170             :  * @param instance_id instance to delete product of
    1171             :  * @param product_id product to delete
    1172             :  * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
    1173             :  *           if locks prevent deletion OR product unknown
    1174             :  */
    1175             : static enum GNUNET_DB_QueryStatus
    1176           6 : postgres_delete_product (void *cls,
    1177             :                          const char *instance_id,
    1178             :                          const char *product_id)
    1179             : {
    1180           6 :   struct PostgresClosure *pg = cls;
    1181           6 :   struct GNUNET_PQ_QueryParam params[] = {
    1182           6 :     GNUNET_PQ_query_param_string (instance_id),
    1183           6 :     GNUNET_PQ_query_param_string (product_id),
    1184             :     GNUNET_PQ_query_param_end
    1185             :   };
    1186             : 
    1187           6 :   check_connection (pg);
    1188           6 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1189             :                                              "delete_product",
    1190             :                                              params);
    1191             : }
    1192             : 
    1193             : 
    1194             : /**
    1195             :  * Insert details about a particular product.
    1196             :  *
    1197             :  * @param cls closure
    1198             :  * @param instance_id instance to insert product for
    1199             :  * @param product_id product identifier of product to insert
    1200             :  * @param pd the product details to insert
    1201             :  * @return database result code
    1202             :  */
    1203             : static enum GNUNET_DB_QueryStatus
    1204           8 : postgres_insert_product (void *cls,
    1205             :                          const char *instance_id,
    1206             :                          const char *product_id,
    1207             :                          const struct TALER_MERCHANTDB_ProductDetails *pd)
    1208             : {
    1209           8 :   struct PostgresClosure *pg = cls;
    1210           8 :   struct GNUNET_PQ_QueryParam params[] = {
    1211           8 :     GNUNET_PQ_query_param_string (instance_id),
    1212           8 :     GNUNET_PQ_query_param_string (product_id),
    1213           8 :     GNUNET_PQ_query_param_string (pd->description),
    1214           8 :     TALER_PQ_query_param_json (pd->description_i18n),
    1215           8 :     GNUNET_PQ_query_param_string (pd->unit),
    1216           8 :     GNUNET_PQ_query_param_string (pd->image),
    1217           8 :     TALER_PQ_query_param_json (pd->taxes),
    1218           8 :     TALER_PQ_query_param_amount (&pd->price),
    1219           8 :     GNUNET_PQ_query_param_uint64 (&pd->total_stock),
    1220           8 :     TALER_PQ_query_param_json (pd->address),
    1221           8 :     GNUNET_PQ_query_param_absolute_time (&pd->next_restock),
    1222             :     GNUNET_PQ_query_param_end
    1223             :   };
    1224             : 
    1225           8 :   check_connection (pg);
    1226           8 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1227             :                                              "insert_product",
    1228             :                                              params);
    1229             : }
    1230             : 
    1231             : 
    1232             : /**
    1233             :  * Update details about a particular product. Note that the
    1234             :  * transaction must enforce that the sold/stocked/lost counters
    1235             :  * are not reduced (i.e. by expanding the WHERE clause on the existing
    1236             :  * values).
    1237             :  *
    1238             :  * @param cls closure
    1239             :  * @param instance_id instance to lookup products for
    1240             :  * @param product_id product to lookup
    1241             :  * @param[out] pd set to the product details on success, can be NULL
    1242             :  *             (in that case we only want to check if the product exists)
    1243             :  *             total_sold in @a pd is ignored, total_lost must not
    1244             :  *             exceed total_stock minus the existing total_sold;
    1245             :  *             total_sold and total_stock must be larger or equal to
    1246             :  *             the existing value;
    1247             :  * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the
    1248             :  *         non-decreasing constraints are not met *or* if the product
    1249             :  *         does not yet exist.
    1250             :  */
    1251             : static enum GNUNET_DB_QueryStatus
    1252           7 : postgres_update_product (void *cls,
    1253             :                          const char *instance_id,
    1254             :                          const char *product_id,
    1255             :                          const struct TALER_MERCHANTDB_ProductDetails *pd)
    1256             : {
    1257           7 :   struct PostgresClosure *pg = cls;
    1258           7 :   struct GNUNET_PQ_QueryParam params[] = {
    1259           7 :     GNUNET_PQ_query_param_string (instance_id), /* $1 */
    1260           7 :     GNUNET_PQ_query_param_string (product_id),
    1261           7 :     GNUNET_PQ_query_param_string (pd->description),
    1262           7 :     TALER_PQ_query_param_json (pd->description_i18n),
    1263           7 :     GNUNET_PQ_query_param_string (pd->unit),
    1264           7 :     GNUNET_PQ_query_param_string (pd->image), /* $6 */
    1265           7 :     TALER_PQ_query_param_json (pd->taxes),
    1266           7 :     TALER_PQ_query_param_amount (&pd->price), /* $8+$9 */
    1267           7 :     GNUNET_PQ_query_param_uint64 (&pd->total_stock),  /* $10 */
    1268           7 :     GNUNET_PQ_query_param_uint64 (&pd->total_lost),
    1269           7 :     TALER_PQ_query_param_json (pd->address),
    1270           7 :     GNUNET_PQ_query_param_absolute_time (&pd->next_restock),
    1271             :     GNUNET_PQ_query_param_end
    1272             :   };
    1273             : 
    1274           7 :   if ( (pd->total_stock < pd->total_lost + pd->total_sold) ||
    1275           7 :        (pd->total_lost < pd->total_lost
    1276           7 :         + pd->total_sold) /* integer overflow */)
    1277             :   {
    1278           0 :     GNUNET_break (0);
    1279           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1280             :   }
    1281           7 :   check_connection (pg);
    1282           7 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1283             :                                              "update_product",
    1284             :                                              params);
    1285             : }
    1286             : 
    1287             : 
    1288             : /**
    1289             :  * Lock stocks of a particular product. Note that the transaction must
    1290             :  * enforce that the "stocked-sold-lost >= locked" constraint holds.
    1291             :  *
    1292             :  * @param cls closure
    1293             :  * @param instance_id instance to lookup products for
    1294             :  * @param product_id product to lookup
    1295             :  * @param uuid the UUID that holds the lock
    1296             :  * @param quantity how many units should be locked
    1297             :  * @param expiration_time when should the lock expire
    1298             :  * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the
    1299             :  *         product is unknown OR if there insufficient stocks remaining
    1300             :  */
    1301             : static enum GNUNET_DB_QueryStatus
    1302           6 : postgres_lock_product (void *cls,
    1303             :                        const char *instance_id,
    1304             :                        const char *product_id,
    1305             :                        const struct GNUNET_Uuid *uuid,
    1306             :                        uint64_t quantity,
    1307             :                        struct GNUNET_TIME_Absolute expiration_time)
    1308             : {
    1309           6 :   struct PostgresClosure *pg = cls;
    1310           6 :   struct GNUNET_PQ_QueryParam params[] = {
    1311           6 :     GNUNET_PQ_query_param_string (instance_id),
    1312           6 :     GNUNET_PQ_query_param_string (product_id),
    1313           6 :     GNUNET_PQ_query_param_auto_from_type (uuid),
    1314           6 :     GNUNET_PQ_query_param_uint64 (&quantity),
    1315           6 :     GNUNET_PQ_query_param_absolute_time (&expiration_time),
    1316             :     GNUNET_PQ_query_param_end
    1317             :   };
    1318             : 
    1319           6 :   check_connection (pg);
    1320           6 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1321             :                                              "lock_product",
    1322             :                                              params);
    1323             : }
    1324             : 
    1325             : 
    1326             : /**
    1327             :  * Release all expired product locks, including
    1328             :  * those from expired offers -- across all
    1329             :  * instances.
    1330             :  *
    1331             :  * @param cls closure
    1332             :  */
    1333             : static void
    1334          37 : postgres_expire_locks (void *cls)
    1335             : {
    1336          37 :   struct PostgresClosure *pg = cls;
    1337          37 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    1338          37 :   struct GNUNET_PQ_QueryParam params[] = {
    1339          37 :     GNUNET_PQ_query_param_absolute_time (&now),
    1340             :     GNUNET_PQ_query_param_end
    1341             :   };
    1342             :   enum GNUNET_DB_QueryStatus qs1;
    1343             :   enum GNUNET_DB_QueryStatus qs2;
    1344             : 
    1345          37 :   check_connection (pg);
    1346          37 :   qs1 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1347             :                                             "unlock_products",
    1348             :                                             params);
    1349          37 :   if (qs1 < 0)
    1350             :   {
    1351           0 :     GNUNET_break (0);
    1352           0 :     return;
    1353             :   }
    1354          37 :   qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1355             :                                             "unlock_orders",
    1356             :                                             params);
    1357          37 :   if (qs2 < 0)
    1358             :   {
    1359           0 :     GNUNET_break (0);
    1360           0 :     return;
    1361             :   }
    1362          37 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1363             :               "Released %d+%d locks\n",
    1364             :               qs1,
    1365             :               qs2);
    1366             : }
    1367             : 
    1368             : 
    1369             : /**
    1370             :  * Delete information about an order.  Note that the transaction must
    1371             :  * enforce that the order is not awaiting payment anymore.
    1372             :  *
    1373             :  * @param cls closure
    1374             :  * @param instance_id instance to delete order of
    1375             :  * @param order_id order to delete
    1376             :  * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
    1377             :  *           if pending payment prevents deletion OR order unknown
    1378             :  */
    1379             : static enum GNUNET_DB_QueryStatus
    1380           7 : postgres_delete_order (void *cls,
    1381             :                        const char *instance_id,
    1382             :                        const char *order_id)
    1383             : {
    1384           7 :   struct PostgresClosure *pg = cls;
    1385           7 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    1386           7 :   struct GNUNET_PQ_QueryParam params[] = {
    1387           7 :     GNUNET_PQ_query_param_string (instance_id),
    1388           7 :     GNUNET_PQ_query_param_string (order_id),
    1389           7 :     GNUNET_PQ_query_param_absolute_time (&now),
    1390             :     GNUNET_PQ_query_param_end
    1391             :   };
    1392             : 
    1393           7 :   check_connection (pg);
    1394           7 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1395             :                                              "delete_order",
    1396             :                                              params);
    1397             : }
    1398             : 
    1399             : 
    1400             : /**
    1401             :  * Retrieve order given its @a order_id and the @a instance_id.
    1402             :  *
    1403             :  * @param cls closure
    1404             :  * @param instance_id instance to obtain order of
    1405             :  * @param order_id order id used to perform the lookup
    1406             :  * @param[out] claim_token the claim token generated for the order,
    1407             :  *             NULL to only test if the order exists
    1408             :  * @param[out] h_post_data set to the hash of the POST data that created the order
    1409             :  * @param[out] contract_terms where to store the retrieved contract terms,
    1410             :  *             NULL to only test if the order exists
    1411             :  * @return transaction status
    1412             :  */
    1413             : static enum GNUNET_DB_QueryStatus
    1414         116 : postgres_lookup_order (void *cls,
    1415             :                        const char *instance_id,
    1416             :                        const char *order_id,
    1417             :                        struct TALER_ClaimTokenP *claim_token,
    1418             :                        struct GNUNET_HashCode *h_post_data,
    1419             :                        json_t **contract_terms)
    1420             : {
    1421         116 :   struct PostgresClosure *pg = cls;
    1422             :   json_t *j;
    1423             :   struct TALER_ClaimTokenP ct;
    1424             :   enum GNUNET_DB_QueryStatus qs;
    1425         116 :   struct GNUNET_PQ_QueryParam params[] = {
    1426         116 :     GNUNET_PQ_query_param_string (instance_id),
    1427         116 :     GNUNET_PQ_query_param_string (order_id),
    1428             :     GNUNET_PQ_query_param_end
    1429             :   };
    1430         116 :   struct GNUNET_PQ_ResultSpec rs[] = {
    1431         116 :     TALER_PQ_result_spec_json ("contract_terms",
    1432             :                                &j),
    1433         116 :     GNUNET_PQ_result_spec_auto_from_type ("claim_token",
    1434             :                                           &ct),
    1435         116 :     GNUNET_PQ_result_spec_auto_from_type ("h_post_data",
    1436             :                                           h_post_data),
    1437             :     GNUNET_PQ_result_spec_end
    1438             :   };
    1439             : 
    1440         116 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1441             :               "Finding contract term, order_id: '%s', instance_id: '%s'.\n",
    1442             :               order_id,
    1443             :               instance_id);
    1444         116 :   check_connection (pg);
    1445         116 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    1446             :                                                  "lookup_order",
    1447             :                                                  params,
    1448             :                                                  rs);
    1449         116 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    1450             :   {
    1451          59 :     if (NULL != contract_terms)
    1452          59 :       *contract_terms = j;
    1453             :     else
    1454           0 :       json_decref (j);
    1455          59 :     if (NULL != claim_token)
    1456          56 :       *claim_token = ct;
    1457             :   }
    1458             :   else
    1459             :   {
    1460             :     /* just to be safe: NULL it */
    1461          57 :     if (NULL != contract_terms)
    1462          54 :       *contract_terms = NULL;
    1463          57 :     if (NULL != claim_token)
    1464          54 :       *claim_token = (struct TALER_ClaimTokenP) { 0 }
    1465             :     ;
    1466             :   }
    1467         116 :   return qs;
    1468             : }
    1469             : 
    1470             : 
    1471             : /**
    1472             :  * Retrieve order summary given its @a order_id and the @a instance_id.
    1473             :  *
    1474             :  * @param cls closure
    1475             :  * @param instance_id instance to obtain order of
    1476             :  * @param order_id order id used to perform the lookup
    1477             :  * @param[out] timestamp when was the order created
    1478             :  * @param[out] order_serial under which serial do we keep this order
    1479             :  * @return transaction status
    1480             :  */
    1481             : static enum GNUNET_DB_QueryStatus
    1482          32 : postgres_lookup_order_summary (void *cls,
    1483             :                                const char *instance_id,
    1484             :                                const char *order_id,
    1485             :                                struct GNUNET_TIME_Absolute *timestamp,
    1486             :                                uint64_t *order_serial)
    1487             : {
    1488          32 :   struct PostgresClosure *pg = cls;
    1489          32 :   struct GNUNET_PQ_QueryParam params[] = {
    1490          32 :     GNUNET_PQ_query_param_string (instance_id),
    1491          32 :     GNUNET_PQ_query_param_string (order_id),
    1492             :     GNUNET_PQ_query_param_end
    1493             :   };
    1494          32 :   struct GNUNET_PQ_ResultSpec rs[] = {
    1495          32 :     GNUNET_PQ_result_spec_uint64 ("order_serial",
    1496             :                                   order_serial),
    1497          32 :     GNUNET_PQ_result_spec_absolute_time ("creation_time",
    1498             :                                          timestamp),
    1499             :     GNUNET_PQ_result_spec_end
    1500             :   };
    1501             : 
    1502          32 :   check_connection (pg);
    1503          32 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    1504             :                                                    "lookup_order_summary",
    1505             :                                                    params,
    1506             :                                                    rs);
    1507             : }
    1508             : 
    1509             : 
    1510             : /**
    1511             :  * Context used for postgres_lookup_orders().
    1512             :  */
    1513             : struct LookupOrdersContext
    1514             : {
    1515             :   /**
    1516             :    * Function to call with the results.
    1517             :    */
    1518             :   TALER_MERCHANTDB_OrdersCallback cb;
    1519             : 
    1520             :   /**
    1521             :    * Closure for @a cb.
    1522             :    */
    1523             :   void *cb_cls;
    1524             : 
    1525             :   /**
    1526             :    * Did database result extraction fail?
    1527             :    */
    1528             :   bool extract_failed;
    1529             : };
    1530             : 
    1531             : 
    1532             : /**
    1533             :  * Function to be called with the results of a SELECT statement
    1534             :  * that has returned @a num_results results about orders.
    1535             :  *
    1536             :  * @param[in,out] cls of type `struct LookupOrdersContext *`
    1537             :  * @param result the postgres result
    1538             :  * @param num_results the number of results in @a result
    1539             :  */
    1540             : static void
    1541         132 : lookup_orders_cb (void *cls,
    1542             :                   PGresult *result,
    1543             :                   unsigned int num_results)
    1544             : {
    1545         132 :   struct LookupOrdersContext *plc = cls;
    1546             : 
    1547        3252 :   for (unsigned int i = 0; i < num_results; i++)
    1548             :   {
    1549             :     char *order_id;
    1550             :     uint64_t order_serial;
    1551             :     struct GNUNET_TIME_Absolute ts;
    1552        3120 :     struct GNUNET_PQ_ResultSpec rs[] = {
    1553        3120 :       GNUNET_PQ_result_spec_string ("order_id",
    1554             :                                     &order_id),
    1555        3120 :       GNUNET_PQ_result_spec_uint64 ("order_serial",
    1556             :                                     &order_serial),
    1557        3120 :       GNUNET_PQ_result_spec_absolute_time ("creation_time",
    1558             :                                            &ts),
    1559             :       GNUNET_PQ_result_spec_end
    1560             :     };
    1561             : 
    1562        3120 :     if (GNUNET_OK !=
    1563        3120 :         GNUNET_PQ_extract_result (result,
    1564             :                                   rs,
    1565             :                                   i))
    1566             :     {
    1567           0 :       GNUNET_break (0);
    1568           0 :       plc->extract_failed = true;
    1569           0 :       return;
    1570             :     }
    1571        3120 :     plc->cb (plc->cb_cls,
    1572             :              order_id,
    1573             :              order_serial,
    1574             :              ts);
    1575        3120 :     GNUNET_PQ_cleanup_result (rs);
    1576             :   }
    1577             : }
    1578             : 
    1579             : 
    1580             : /**
    1581             :  * Retrieve orders given the @a instance_id.
    1582             :  *
    1583             :  * @param cls closure
    1584             :  * @param instance_id instance to obtain order of
    1585             :  * @param of filter to apply when looking up orders
    1586             :  * @param cb callback to pass all the orders that are found
    1587             :  * @param cb_cls closure for @a cb
    1588             :  * @return transaction status
    1589             :  */
    1590             : static enum GNUNET_DB_QueryStatus
    1591         132 : postgres_lookup_orders (void *cls,
    1592             :                         const char *instance_id,
    1593             :                         const struct TALER_MERCHANTDB_OrderFilter *of,
    1594             :                         TALER_MERCHANTDB_OrdersCallback cb,
    1595             :                         void *cb_cls)
    1596             : {
    1597         132 :   struct PostgresClosure *pg = cls;
    1598         132 :   struct LookupOrdersContext plc = {
    1599             :     .cb = cb,
    1600             :     .cb_cls = cb_cls
    1601             :   };
    1602         132 :   uint64_t limit = (of->delta > 0) ? of->delta : -of->delta;
    1603             :   uint8_t paid;
    1604             :   uint8_t refunded;
    1605             :   uint8_t wired;
    1606         132 :   struct GNUNET_PQ_QueryParam params[] = {
    1607         132 :     GNUNET_PQ_query_param_string (instance_id),
    1608         132 :     GNUNET_PQ_query_param_uint64 (&limit),
    1609         132 :     GNUNET_PQ_query_param_uint64 (&of->start_row),
    1610         132 :     GNUNET_PQ_query_param_absolute_time (&of->date),
    1611         132 :     GNUNET_PQ_query_param_auto_from_type (&paid),
    1612         132 :     GNUNET_PQ_query_param_auto_from_type (&refunded),
    1613         132 :     GNUNET_PQ_query_param_auto_from_type (&wired),
    1614             :     GNUNET_PQ_query_param_end
    1615             :   };
    1616             :   enum GNUNET_DB_QueryStatus qs;
    1617             :   char stmt[128];
    1618             : 
    1619         132 :   paid = (TALER_EXCHANGE_YNA_YES == of->paid);
    1620         132 :   refunded = (TALER_EXCHANGE_YNA_YES == of->refunded);
    1621         132 :   wired = (TALER_EXCHANGE_YNA_YES == of->wired);
    1622             :   /* painfully many cases..., note that "_xxx" being present in 'stmt' merely
    1623             :      means that we filter by that variable, the value we filter for is
    1624             :      computed above */
    1625         528 :   GNUNET_snprintf (stmt,
    1626             :                    sizeof (stmt),
    1627             :                    "lookup_orders_%s%s%s%s",
    1628         132 :                    (of->delta > 0) ? "inc" : "dec",
    1629         132 :                    (TALER_EXCHANGE_YNA_ALL == of->paid) ? "" : "_paid",
    1630         132 :                    (TALER_EXCHANGE_YNA_ALL == of->refunded) ? "" :
    1631             :                    "_refunded",
    1632         132 :                    (TALER_EXCHANGE_YNA_ALL == of->wired) ? "" : "_wired");
    1633         132 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    1634             :                                              stmt,
    1635             :                                              params,
    1636             :                                              &lookup_orders_cb,
    1637             :                                              &plc);
    1638         132 :   if (plc.extract_failed)
    1639           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1640         132 :   return qs;
    1641             : }
    1642             : 
    1643             : 
    1644             : /**
    1645             :  * Insert order into the DB.
    1646             :  *
    1647             :  * @param cls closure
    1648             :  * @param instance_id identifies the instance responsible for the order
    1649             :  * @param order_id alphanumeric string that uniquely identifies the proposal
    1650             :  * @param h_post_data hash of the POST data for idempotency checks
    1651             :  * @param pay_deadline how long does the customer have to pay for the order
    1652             :  * @param claim_token token to use for access control
    1653             :  * @param contract_terms proposal data to store
    1654             :  * @return transaction status
    1655             :  */
    1656             : static enum GNUNET_DB_QueryStatus
    1657         102 : postgres_insert_order (void *cls,
    1658             :                        const char *instance_id,
    1659             :                        const char *order_id,
    1660             :                        const struct GNUNET_HashCode *h_post_data,
    1661             :                        struct GNUNET_TIME_Absolute pay_deadline,
    1662             :                        const struct TALER_ClaimTokenP *claim_token,
    1663             :                        const json_t *contract_terms)
    1664             : {
    1665         102 :   struct PostgresClosure *pg = cls;
    1666             :   struct GNUNET_TIME_Absolute now;
    1667         102 :   struct GNUNET_PQ_QueryParam params[] = {
    1668         102 :     GNUNET_PQ_query_param_string (instance_id),
    1669         102 :     GNUNET_PQ_query_param_string (order_id),
    1670         102 :     GNUNET_PQ_query_param_absolute_time (&pay_deadline),
    1671         102 :     GNUNET_PQ_query_param_auto_from_type (claim_token),
    1672         102 :     GNUNET_PQ_query_param_auto_from_type (h_post_data),
    1673         102 :     GNUNET_PQ_query_param_absolute_time (&now),
    1674         102 :     TALER_PQ_query_param_json (contract_terms),
    1675             :     GNUNET_PQ_query_param_end
    1676             :   };
    1677             : 
    1678         102 :   now = GNUNET_TIME_absolute_get ();
    1679         102 :   (void) GNUNET_TIME_round_abs (&now);
    1680         102 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1681             :               "inserting order: order_id: %s, instance_id: %s.\n",
    1682             :               order_id,
    1683             :               instance_id);
    1684         102 :   check_connection (pg);
    1685         102 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1686             :                                              "insert_order",
    1687             :                                              params);
    1688             : }
    1689             : 
    1690             : 
    1691             : /**
    1692             :  * Release an inventory lock by UUID. Releases ALL stocks locked under
    1693             :  * the given UUID.
    1694             :  *
    1695             :  * @param cls closure
    1696             :  * @param uuid the UUID to release locks for
    1697             :  * @return transaction status,
    1698             :  *   #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS means there are no locks under @a uuid
    1699             :  *   #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success
    1700             :  */
    1701             : static enum GNUNET_DB_QueryStatus
    1702           3 : postgres_unlock_inventory (void *cls,
    1703             :                            const struct GNUNET_Uuid *uuid)
    1704             : {
    1705           3 :   struct PostgresClosure *pg = cls;
    1706           3 :   struct GNUNET_PQ_QueryParam params[] = {
    1707           3 :     GNUNET_PQ_query_param_auto_from_type (uuid),
    1708             :     GNUNET_PQ_query_param_end
    1709             :   };
    1710             : 
    1711           3 :   check_connection (pg);
    1712           3 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1713             :                                              "unlock_inventory",
    1714             :                                              params);
    1715             : }
    1716             : 
    1717             : 
    1718             : /**
    1719             :  * Lock inventory stock to a particular order.
    1720             :  *
    1721             :  * @param cls closure
    1722             :  * @param instance_id identifies the instance responsible for the order
    1723             :  * @param order_id alphanumeric string that uniquely identifies the order
    1724             :  * @param product_id uniquely identifies the product to be locked
    1725             :  * @param quantity how many units should be locked to the @a order_id
    1726             :  * @return transaction status,
    1727             :  *   #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS means there are insufficient stocks
    1728             :  *   #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success
    1729             :  */
    1730             : static enum GNUNET_DB_QueryStatus
    1731           3 : postgres_insert_order_lock (void *cls,
    1732             :                             const char *instance_id,
    1733             :                             const char *order_id,
    1734             :                             const char *product_id,
    1735             :                             uint64_t quantity)
    1736             : {
    1737           3 :   struct PostgresClosure *pg = cls;
    1738           3 :   struct GNUNET_PQ_QueryParam params[] = {
    1739           3 :     GNUNET_PQ_query_param_string (instance_id),
    1740           3 :     GNUNET_PQ_query_param_string (order_id),
    1741           3 :     GNUNET_PQ_query_param_string (product_id),
    1742           3 :     GNUNET_PQ_query_param_uint64 (&quantity),
    1743             :     GNUNET_PQ_query_param_end
    1744             :   };
    1745             : 
    1746           3 :   check_connection (pg);
    1747           3 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1748             :                                              "insert_order_lock",
    1749             :                                              params);
    1750             : }
    1751             : 
    1752             : 
    1753             : /**
    1754             :  * Retrieve contract terms given its @a order_id
    1755             :  *
    1756             :  * @param cls closure
    1757             :  * @param instance_id instance's identifier
    1758             :  * @param order_id order_id used to lookup.
    1759             :  * @param[out] contract_terms where to store the result, NULL to only check for existence
    1760             :  * @param[out] order_serial set to the order's serial number
    1761             :  * @param[out] claim_token set to the claim token, NULL to only check for existence
    1762             :  * @return transaction status
    1763             :  */
    1764             : static enum GNUNET_DB_QueryStatus
    1765         176 : postgres_lookup_contract_terms (void *cls,
    1766             :                                 const char *instance_id,
    1767             :                                 const char *order_id,
    1768             :                                 json_t **contract_terms,
    1769             :                                 uint64_t *order_serial,
    1770             :                                 struct TALER_ClaimTokenP *claim_token)
    1771             : {
    1772         176 :   struct PostgresClosure *pg = cls;
    1773             :   enum GNUNET_DB_QueryStatus qs;
    1774             :   struct TALER_ClaimTokenP ct;
    1775         176 :   struct GNUNET_PQ_QueryParam params[] = {
    1776         176 :     GNUNET_PQ_query_param_string (instance_id),
    1777         176 :     GNUNET_PQ_query_param_string (order_id),
    1778             :     GNUNET_PQ_query_param_end
    1779             :   };
    1780         176 :   struct GNUNET_PQ_ResultSpec rs[4] = {
    1781             :     /* contract_terms must be first! */
    1782         176 :     TALER_PQ_result_spec_json ("contract_terms",
    1783             :                                contract_terms),
    1784         176 :     GNUNET_PQ_result_spec_uint64 ("order_serial",
    1785             :                                   order_serial),
    1786         176 :     GNUNET_PQ_result_spec_auto_from_type ("claim_token",
    1787             :                                           &ct),
    1788             :     GNUNET_PQ_result_spec_end
    1789             :   };
    1790             : 
    1791         176 :   check_connection (pg);
    1792         176 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    1793             :                                                  "lookup_contract_terms",
    1794             :                                                  params,
    1795             :                                                  (NULL != contract_terms)
    1796             :                                                    ? rs
    1797             :                                                    : &rs[1]);
    1798         176 :   if (NULL != claim_token)
    1799          64 :     *claim_token = ct;
    1800         176 :   return qs;
    1801             : }
    1802             : 
    1803             : 
    1804             : /**
    1805             :  * Store contract terms given its @a order_id. Note that some attributes are
    1806             :  * expected to be calculated inside of the function, like the hash of the
    1807             :  * contract terms (to be hashed), the creation_time and pay_deadline (to be
    1808             :  * obtained from the merchant_orders table). The "session_id" should be
    1809             :  * initially set to the empty string.  The "fulfillment_url" and "refund_deadline"
    1810             :  * must be extracted from @a contract_terms.
    1811             :  *
    1812             :  * @param cls closure
    1813             :  * @param instance_id instance's identifier
    1814             :  * @param order_id order_id used to store
    1815             :  * @param contract_terms contract terms to store
    1816             :  * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms
    1817             :  *          is malformed
    1818             :  */
    1819             : static enum GNUNET_DB_QueryStatus
    1820          64 : postgres_insert_contract_terms (void *cls,
    1821             :                                 const char *instance_id,
    1822             :                                 const char *order_id,
    1823             :                                 json_t *contract_terms)
    1824             : {
    1825          64 :   struct PostgresClosure *pg = cls;
    1826             :   struct GNUNET_TIME_Absolute pay_deadline;
    1827             :   struct GNUNET_TIME_Absolute refund_deadline;
    1828             :   const char *fulfillment_url;
    1829             :   struct GNUNET_HashCode h_contract_terms;
    1830             : 
    1831          64 :   if (GNUNET_OK !=
    1832          64 :       TALER_JSON_contract_hash (contract_terms,
    1833             :                                 &h_contract_terms))
    1834             :   {
    1835           0 :     GNUNET_break (0);
    1836           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1837             :   }
    1838             : 
    1839             :   {
    1840             :     struct GNUNET_JSON_Specification spec[] = {
    1841          64 :       TALER_JSON_spec_absolute_time ("pay_deadline",
    1842             :                                      &pay_deadline),
    1843          64 :       TALER_JSON_spec_absolute_time ("refund_deadline",
    1844             :                                      &refund_deadline),
    1845          64 :       GNUNET_JSON_spec_end ()
    1846             :     };
    1847             :     enum GNUNET_GenericReturnValue res;
    1848             : 
    1849          64 :     res = TALER_MHD_parse_json_data (NULL,
    1850             :                                      contract_terms,
    1851             :                                      spec);
    1852          64 :     if (GNUNET_OK != res)
    1853             :     {
    1854           0 :       GNUNET_break (0);
    1855           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    1856             :     }
    1857             :   }
    1858             : 
    1859             :   fulfillment_url =
    1860          64 :     json_string_value (json_object_get (contract_terms,
    1861             :                                         "fulfillment_url"));
    1862          64 :   check_connection (pg);
    1863             :   {
    1864          64 :     struct GNUNET_PQ_QueryParam params[] = {
    1865          64 :       GNUNET_PQ_query_param_string (instance_id),
    1866          64 :       GNUNET_PQ_query_param_string (order_id),
    1867          64 :       TALER_PQ_query_param_json (contract_terms),
    1868          64 :       GNUNET_PQ_query_param_auto_from_type (&h_contract_terms),
    1869          64 :       GNUNET_PQ_query_param_absolute_time (&pay_deadline),
    1870          64 :       GNUNET_PQ_query_param_absolute_time (&refund_deadline),
    1871             :       (NULL == fulfillment_url)
    1872           1 :       ? GNUNET_PQ_query_param_null ()
    1873          63 :       : GNUNET_PQ_query_param_string (fulfillment_url),
    1874             :       GNUNET_PQ_query_param_end
    1875             :     };
    1876             : 
    1877          64 :     return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1878             :                                                "insert_contract_terms",
    1879             :                                                params);
    1880             :   }
    1881             : }
    1882             : 
    1883             : 
    1884             : /**
    1885             :  * Update the contract terms stored for @a order_id. Note that some attributes are
    1886             :  * expected to be calculated inside of the function, like the hash of the
    1887             :  * contract terms (to be hashed), the creation_time and pay_deadline (to be
    1888             :  * obtained from the merchant_orders table). The "session_id" should be
    1889             :  * initially set to the empty string.  The "fulfillment_url" and "refund_deadline"
    1890             :  * must be extracted from @a contract_terms.
    1891             :  *
    1892             :  * @param cls closure
    1893             :  * @param instance_id instance's identifier
    1894             :  * @param order_id order_id used to store
    1895             :  * @param contract_terms contract to store
    1896             :  * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms
    1897             :  *          is malformed
    1898             :  */
    1899             : static enum GNUNET_DB_QueryStatus
    1900           4 : postgres_update_contract_terms (void *cls,
    1901             :                                 const char *instance_id,
    1902             :                                 const char *order_id,
    1903             :                                 json_t *contract_terms)
    1904             : {
    1905           4 :   struct PostgresClosure *pg = cls;
    1906             :   struct GNUNET_TIME_Absolute pay_deadline;
    1907             :   struct GNUNET_TIME_Absolute refund_deadline;
    1908           4 :   const char *fulfillment_url = NULL;
    1909             :   struct GNUNET_HashCode h_contract_terms;
    1910             : 
    1911           4 :   if (GNUNET_OK !=
    1912           4 :       TALER_JSON_contract_hash (contract_terms,
    1913             :                                 &h_contract_terms))
    1914             :   {
    1915           0 :     GNUNET_break (0);
    1916           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1917             :   }
    1918             : 
    1919             :   {
    1920             :     struct GNUNET_JSON_Specification spec[] = {
    1921           4 :       TALER_JSON_spec_absolute_time ("pay_deadline",
    1922             :                                      &pay_deadline),
    1923           4 :       TALER_JSON_spec_absolute_time ("refund_deadline",
    1924             :                                      &refund_deadline),
    1925           4 :       GNUNET_JSON_spec_mark_optional (
    1926             :         GNUNET_JSON_spec_string ("fulfillment_url",
    1927             :                                  &fulfillment_url)),
    1928           4 :       GNUNET_JSON_spec_end ()
    1929             :     };
    1930             :     enum GNUNET_GenericReturnValue res;
    1931             : 
    1932           4 :     res = TALER_MHD_parse_json_data (NULL,
    1933             :                                      contract_terms,
    1934             :                                      spec);
    1935           4 :     if (GNUNET_OK != res)
    1936             :     {
    1937           0 :       GNUNET_break (0);
    1938           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    1939             :     }
    1940             :   }
    1941             : 
    1942           4 :   check_connection (pg);
    1943             :   {
    1944           8 :     struct GNUNET_PQ_QueryParam params[] = {
    1945           4 :       GNUNET_PQ_query_param_string (instance_id),
    1946           4 :       GNUNET_PQ_query_param_string (order_id),
    1947           4 :       TALER_PQ_query_param_json (contract_terms),
    1948           4 :       GNUNET_PQ_query_param_auto_from_type (&h_contract_terms),
    1949           4 :       GNUNET_PQ_query_param_absolute_time (&pay_deadline),
    1950           4 :       GNUNET_PQ_query_param_absolute_time (&refund_deadline),
    1951           4 :       (NULL == fulfillment_url)
    1952           0 :       ? GNUNET_PQ_query_param_null ()
    1953           4 :       : GNUNET_PQ_query_param_string (fulfillment_url),
    1954             :       GNUNET_PQ_query_param_end
    1955             :     };
    1956             : 
    1957           4 :     return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1958             :                                                "update_contract_terms",
    1959             :                                                params);
    1960             :   }
    1961             : }
    1962             : 
    1963             : 
    1964             : /**
    1965             :  * Delete information about a contract. Note that the transaction must
    1966             :  * enforce that the contract is not awaiting payment anymore AND was not
    1967             :  * paid, or is past the legal expiration.
    1968             :  *
    1969             :  * @param cls closure
    1970             :  * @param instance_id instance to delete order of
    1971             :  * @param order_id order to delete
    1972             :  * @param legal_expiration how long do we need to keep (paid) contracts on
    1973             :  *          file for legal reasons (i.e. taxation)
    1974             :  * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
    1975             :  *           if locks prevent deletion OR order unknown
    1976             :  */
    1977             : static enum GNUNET_DB_QueryStatus
    1978           5 : postgres_delete_contract_terms (void *cls,
    1979             :                                 const char *instance_id,
    1980             :                                 const char *order_id,
    1981             :                                 struct GNUNET_TIME_Relative legal_expiration)
    1982             : {
    1983           5 :   struct PostgresClosure *pg = cls;
    1984           5 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    1985           5 :   struct GNUNET_PQ_QueryParam params[] = {
    1986           5 :     GNUNET_PQ_query_param_string (instance_id),
    1987           5 :     GNUNET_PQ_query_param_string (order_id),
    1988           5 :     GNUNET_PQ_query_param_relative_time (&legal_expiration),
    1989           5 :     GNUNET_PQ_query_param_absolute_time (&now),
    1990             :     GNUNET_PQ_query_param_end
    1991             :   };
    1992             : 
    1993           5 :   check_connection (pg);
    1994           5 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    1995             :                                              "delete_contract_terms",
    1996             :                                              params);
    1997             : }
    1998             : 
    1999             : 
    2000             : /**
    2001             :  * Closure for #lookup_deposits_cb().
    2002             :  */
    2003             : struct LookupDepositsContext
    2004             : {
    2005             :   /**
    2006             :    * Function to call with results.
    2007             :    */
    2008             :   TALER_MERCHANTDB_DepositsCallback cb;
    2009             : 
    2010             :   /**
    2011             :    * Closure for @e cls.
    2012             :    */
    2013             :   void *cb_cls;
    2014             : 
    2015             :   /**
    2016             :    * Plugin context.
    2017             :    */
    2018             :   struct PostgresClosure *pg;
    2019             : 
    2020             :   /**
    2021             :    * Transaction status (set).
    2022             :    */
    2023             :   enum GNUNET_DB_QueryStatus qs;
    2024             : };
    2025             : 
    2026             : 
    2027             : /**
    2028             :  * Function to be called with the results of a SELECT statement
    2029             :  * that has returned @a num_results results.
    2030             :  *
    2031             :  * @param[in,out] cls of type `struct LookupDepositsContext *`
    2032             :  * @param result the postgres result
    2033             :  * @param num_results the number of results in @a result
    2034             :  */
    2035             : static void
    2036          57 : lookup_deposits_cb (void *cls,
    2037             :                     PGresult *result,
    2038             :                     unsigned int num_results)
    2039             : {
    2040          57 :   struct LookupDepositsContext *ldc = cls;
    2041          57 :   struct PostgresClosure *pg = ldc->pg;
    2042             : 
    2043          96 :   for (unsigned int i = 0; i<num_results; i++)
    2044             :   {
    2045             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    2046             :     struct TALER_Amount amount_with_fee;
    2047             :     struct TALER_Amount deposit_fee;
    2048             :     struct TALER_Amount refund_fee;
    2049             :     struct TALER_Amount wire_fee;
    2050             :     char *exchange_url;
    2051          39 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2052          39 :       GNUNET_PQ_result_spec_string ("exchange_url",
    2053             :                                     &exchange_url),
    2054          39 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2055             :                                             &coin_pub),
    2056          39 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    2057             :                                    &amount_with_fee),
    2058          39 :       TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
    2059             :                                    &deposit_fee),
    2060          39 :       TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
    2061             :                                    &refund_fee),
    2062          39 :       TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
    2063             :                                    &wire_fee),
    2064             :       GNUNET_PQ_result_spec_end
    2065             :     };
    2066             : 
    2067          39 :     if (GNUNET_OK !=
    2068          39 :         GNUNET_PQ_extract_result (result,
    2069             :                                   rs,
    2070             :                                   i))
    2071             :     {
    2072           0 :       GNUNET_break (0);
    2073           0 :       ldc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2074           0 :       return;
    2075             :     }
    2076          39 :     ldc->cb (ldc->cb_cls,
    2077             :              exchange_url,
    2078             :              &coin_pub,
    2079             :              &amount_with_fee,
    2080             :              &deposit_fee,
    2081             :              &refund_fee,
    2082             :              &wire_fee);
    2083          39 :     GNUNET_PQ_cleanup_result (rs);
    2084             :   }
    2085          57 :   ldc->qs = num_results;
    2086             : }
    2087             : 
    2088             : 
    2089             : /**
    2090             :  * Lookup information about coins that were successfully deposited for a
    2091             :  * given contract.
    2092             :  *
    2093             :  * @param cls closure
    2094             :  * @param instance_id instance to lookup deposits for
    2095             :  * @param h_contract_terms proposal data's hashcode
    2096             :  * @param cb function to call with payment data
    2097             :  * @param cb_cls closure for @a cb
    2098             :  * @return transaction status
    2099             :  */
    2100             : static enum GNUNET_DB_QueryStatus
    2101          57 : postgres_lookup_deposits (void *cls,
    2102             :                           const char *instance_id,
    2103             :                           const struct GNUNET_HashCode *h_contract_terms,
    2104             :                           TALER_MERCHANTDB_DepositsCallback cb,
    2105             :                           void *cb_cls)
    2106             : {
    2107          57 :   struct PostgresClosure *pg = cls;
    2108          57 :   struct GNUNET_PQ_QueryParam params[] = {
    2109          57 :     GNUNET_PQ_query_param_string (instance_id),
    2110          57 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2111             :     GNUNET_PQ_query_param_end
    2112             :   };
    2113          57 :   struct LookupDepositsContext ldc = {
    2114             :     .cb = cb,
    2115             :     .cb_cls = cb_cls,
    2116             :     .pg = pg
    2117             :   };
    2118             :   enum GNUNET_DB_QueryStatus qs;
    2119             : 
    2120             :   /* no preflight check here, run in its own transaction by the caller! */
    2121          57 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2122             :               "Finding deposits for h_contract_terms '%s'\n",
    2123             :               GNUNET_h2s (h_contract_terms));
    2124          57 :   check_connection (pg);
    2125          57 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    2126             :                                              "lookup_deposits",
    2127             :                                              params,
    2128             :                                              &lookup_deposits_cb,
    2129             :                                              &ldc);
    2130          57 :   if (qs <= 0)
    2131          20 :     return qs;
    2132          37 :   return ldc.qs;
    2133             : }
    2134             : 
    2135             : 
    2136             : /**
    2137             :  * Insert an exchange signing key into our database.
    2138             :  *
    2139             :  * @param cls closure
    2140             :  * @param master_pub exchange master public key used for @a master_sig
    2141             :  * @param exchange_pub exchange signing key to insert
    2142             :  * @param start_date when does the signing key become valid
    2143             :  * @param expire_date when does the signing key stop being used
    2144             :  * @param end_date when does the signing key become void as proof
    2145             :  * @param master_sig signature of @a master_pub over the @a exchange_pub and the dates
    2146             :  */
    2147             : static enum GNUNET_DB_QueryStatus
    2148          22 : postgres_insert_exchange_signkey (
    2149             :   void *cls,
    2150             :   const struct TALER_MasterPublicKeyP *master_pub,
    2151             :   const struct TALER_ExchangePublicKeyP *exchange_pub,
    2152             :   struct GNUNET_TIME_Absolute start_date,
    2153             :   struct GNUNET_TIME_Absolute expire_date,
    2154             :   struct GNUNET_TIME_Absolute end_date,
    2155             :   const struct TALER_MasterSignatureP *master_sig)
    2156             : {
    2157          22 :   struct PostgresClosure *pg = cls;
    2158          22 :   struct GNUNET_PQ_QueryParam params[] = {
    2159          22 :     GNUNET_PQ_query_param_auto_from_type (master_pub),
    2160          22 :     GNUNET_PQ_query_param_auto_from_type (exchange_pub),
    2161          22 :     GNUNET_PQ_query_param_absolute_time (&start_date),
    2162          22 :     GNUNET_PQ_query_param_absolute_time (&expire_date),
    2163          22 :     GNUNET_PQ_query_param_absolute_time (&end_date),
    2164          22 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
    2165             :     GNUNET_PQ_query_param_end
    2166             :   };
    2167             : 
    2168          22 :   check_connection (pg);
    2169          22 :   postgres_preflight (pg);
    2170          22 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2171             :                                              "insert_exchange_signkey",
    2172             :                                              params);
    2173             : 
    2174             : }
    2175             : 
    2176             : 
    2177             : /**
    2178             :  * Insert payment confirmation from the exchange into the database.
    2179             :  *
    2180             :  * @param cls closure
    2181             :  * @param instance_id instance to lookup deposits for
    2182             :  * @param deposit_timestamp time when the exchange generated the deposit confirmation
    2183             :  * @param h_contract_terms proposal data's hashcode
    2184             :  * @param coin_pub public key of the coin
    2185             :  * @param exchange_url URL of the exchange that issued @a coin_pub
    2186             :  * @param amount_with_fee amount the exchange will deposit for this coin
    2187             :  * @param deposit_fee fee the exchange will charge for this coin
    2188             :  * @param wire_fee wire fee the exchange charges
    2189             :  * @param refund_fee fee the exchange charges to refund this coin
    2190             :  * @param h_wire hash of the wire details of the target account of the merchant
    2191             :  * @param exchange_sig signature from exchange that coin was accepted
    2192             :  * @param exchange_pub signgin key that was used for @a exchange_sig
    2193             :  * @return transaction status
    2194             :  */
    2195             : static enum GNUNET_DB_QueryStatus
    2196          42 : postgres_insert_deposit (void *cls,
    2197             :                          const char *instance_id,
    2198             :                          struct GNUNET_TIME_Absolute deposit_timestamp,
    2199             :                          const struct GNUNET_HashCode *h_contract_terms,
    2200             :                          const struct TALER_CoinSpendPublicKeyP *coin_pub,
    2201             :                          const char *exchange_url,
    2202             :                          const struct TALER_Amount *amount_with_fee,
    2203             :                          const struct TALER_Amount *deposit_fee,
    2204             :                          const struct TALER_Amount *refund_fee,
    2205             :                          const struct TALER_Amount *wire_fee,
    2206             :                          const struct GNUNET_HashCode *h_wire,
    2207             :                          const struct TALER_ExchangeSignatureP *exchange_sig,
    2208             :                          const struct TALER_ExchangePublicKeyP *exchange_pub)
    2209             : {
    2210          42 :   struct PostgresClosure *pg = cls;
    2211          42 :   struct GNUNET_PQ_QueryParam params[] = {
    2212          42 :     GNUNET_PQ_query_param_string (instance_id),
    2213          42 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2214          42 :     GNUNET_PQ_query_param_absolute_time (&deposit_timestamp), /* $3 */
    2215          42 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    2216          42 :     GNUNET_PQ_query_param_string (exchange_url),
    2217          42 :     TALER_PQ_query_param_amount (amount_with_fee), /* $6/$7 */
    2218          42 :     TALER_PQ_query_param_amount (deposit_fee),  /* $8, $9 */
    2219          42 :     TALER_PQ_query_param_amount (refund_fee), /* $10, $11 */
    2220          42 :     TALER_PQ_query_param_amount (wire_fee),  /* $12, $13 */
    2221          42 :     GNUNET_PQ_query_param_auto_from_type (h_wire), /* $14 */
    2222          42 :     GNUNET_PQ_query_param_auto_from_type (exchange_sig), /* $15 */
    2223          42 :     GNUNET_PQ_query_param_auto_from_type (exchange_pub), /* $16 */
    2224             :     GNUNET_PQ_query_param_end
    2225             :   };
    2226             : 
    2227             :   /* no preflight check here, run in transaction by caller! */
    2228          42 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2229             :               "Storing deposit for instance `%s' h_contract_terms `%s', coin_pub: `%s', amount_with_fee: %s\n",
    2230             :               instance_id,
    2231             :               GNUNET_h2s (h_contract_terms),
    2232             :               TALER_B2S (coin_pub),
    2233             :               TALER_amount2s (amount_with_fee));
    2234          42 :   check_connection (pg);
    2235          42 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2236             :                                              "insert_deposit",
    2237             :                                              params);
    2238             : 
    2239             : }
    2240             : 
    2241             : 
    2242             : /**
    2243             :  * Closure for #lookup_refunds_cb().
    2244             :  */
    2245             : struct LookupRefundsContext
    2246             : {
    2247             :   /**
    2248             :    * Function to call for each refund.
    2249             :    */
    2250             :   TALER_MERCHANTDB_RefundCallback rc;
    2251             : 
    2252             :   /**
    2253             :    * Closure for @e rc.
    2254             :    */
    2255             :   void *rc_cls;
    2256             : 
    2257             :   /**
    2258             :    * Plugin context.
    2259             :    */
    2260             :   struct PostgresClosure *pg;
    2261             : 
    2262             :   /**
    2263             :    * Transaction result.
    2264             :    */
    2265             :   enum GNUNET_DB_QueryStatus qs;
    2266             : };
    2267             : 
    2268             : 
    2269             : /**
    2270             :  * Function to be called with the results of a SELECT statement
    2271             :  * that has returned @a num_results results.
    2272             :  *
    2273             :  * @param cls of type `struct LookupRefundsContext *`
    2274             :  * @param result the postgres result
    2275             :  * @param num_results the number of results in @a result
    2276             :  */
    2277             : static void
    2278          40 : lookup_refunds_cb (void *cls,
    2279             :                    PGresult *result,
    2280             :                    unsigned int num_results)
    2281             : {
    2282          40 :   struct LookupRefundsContext *lrc = cls;
    2283          40 :   struct PostgresClosure *pg = lrc->pg;
    2284             : 
    2285          41 :   for (unsigned int i = 0; i<num_results; i++)
    2286             :   {
    2287             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    2288             :     struct TALER_Amount refund_amount;
    2289           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2290           1 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2291             :                                             &coin_pub),
    2292           1 :       TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
    2293             :                                    &refund_amount),
    2294             :       GNUNET_PQ_result_spec_end
    2295             :     };
    2296             : 
    2297           1 :     if (GNUNET_OK !=
    2298           1 :         GNUNET_PQ_extract_result (result,
    2299             :                                   rs,
    2300             :                                   i))
    2301             :     {
    2302           0 :       GNUNET_break (0);
    2303           0 :       lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2304           0 :       return;
    2305             :     }
    2306           1 :     lrc->rc (lrc->rc_cls,
    2307             :              &coin_pub,
    2308             :              &refund_amount);
    2309           1 :     GNUNET_PQ_cleanup_result (rs); /* technically useless here */
    2310             :   }
    2311          40 :   lrc->qs = num_results;
    2312             : }
    2313             : 
    2314             : 
    2315             : /**
    2316             :  * Obtain refunds associated with a contract.
    2317             :  *
    2318             :  * @param cls closure, typically a connection to the db
    2319             :  * @param instance_id instance to lookup refunds for
    2320             :  * @param h_contract_terms hash code of the contract
    2321             :  * @param rc function to call for each coin on which there is a refund
    2322             :  * @param rc_cls closure for @a rc
    2323             :  * @return transaction status
    2324             :  */
    2325             : static enum GNUNET_DB_QueryStatus
    2326          40 : postgres_lookup_refunds (void *cls,
    2327             :                          const char *instance_id,
    2328             :                          const struct GNUNET_HashCode *h_contract_terms,
    2329             :                          TALER_MERCHANTDB_RefundCallback rc,
    2330             :                          void *rc_cls)
    2331             : {
    2332          40 :   struct PostgresClosure *pg = cls;
    2333          40 :   struct GNUNET_PQ_QueryParam params[] = {
    2334          40 :     GNUNET_PQ_query_param_string (instance_id),
    2335          40 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2336             :     GNUNET_PQ_query_param_end
    2337             :   };
    2338          40 :   struct LookupRefundsContext lrc = {
    2339             :     .rc = rc,
    2340             :     .rc_cls = rc_cls,
    2341             :     .pg = pg
    2342             :   };
    2343             :   enum GNUNET_DB_QueryStatus qs;
    2344             : 
    2345             :   /* no preflight check here, run in transaction by caller! */
    2346          40 :   TALER_LOG_DEBUG ("Looking for refund of h_contract_terms %s at `%s'\n",
    2347             :                    GNUNET_h2s (h_contract_terms),
    2348             :                    instance_id);
    2349          40 :   check_connection (pg);
    2350          40 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    2351             :                                              "lookup_refunds",
    2352             :                                              params,
    2353             :                                              &lookup_refunds_cb,
    2354             :                                              &lrc);
    2355          40 :   if (0 >= qs)
    2356          39 :     return qs;
    2357           1 :   return lrc.qs;
    2358             : }
    2359             : 
    2360             : 
    2361             : /**
    2362             :  * Mark contract as paid and store the current @a session_id
    2363             :  * for which the contract was paid. Deletes the underlying order
    2364             :  * and marks the locked stocks of the order as sold.
    2365             :  *
    2366             :  * @param cls closure
    2367             :  * @param instance_id instance to mark contract as paid for
    2368             :  * @param h_contract_terms hash of the contract that is now paid
    2369             :  * @param session_id the session that paid the contract
    2370             :  * @return transaction status
    2371             :  */
    2372             : static enum GNUNET_DB_QueryStatus
    2373          39 : postgres_mark_contract_paid (void *cls,
    2374             :                              const char *instance_id,
    2375             :                              const struct GNUNET_HashCode *h_contract_terms,
    2376             :                              const char *session_id)
    2377             : {
    2378          39 :   struct PostgresClosure *pg = cls;
    2379          39 :   struct GNUNET_PQ_QueryParam params[] = {
    2380          39 :     GNUNET_PQ_query_param_string (instance_id),
    2381          39 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2382          39 :     GNUNET_PQ_query_param_string (session_id),
    2383             :     GNUNET_PQ_query_param_end
    2384             :   };
    2385          39 :   struct GNUNET_PQ_QueryParam uparams[] = {
    2386          39 :     GNUNET_PQ_query_param_string (instance_id),
    2387          39 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2388             :     GNUNET_PQ_query_param_end
    2389             :   };
    2390             :   enum GNUNET_DB_QueryStatus qs;
    2391             : 
    2392             :   /* Session ID must always be given by the caller. */
    2393          39 :   GNUNET_assert (NULL != session_id);
    2394             : 
    2395             :   /* no preflight check here, run in transaction by caller! */
    2396          39 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2397             :               "Marking h_contract_terms '%s' of %s as paid for session `%s'\n",
    2398             :               GNUNET_h2s (h_contract_terms),
    2399             :               instance_id,
    2400             :               session_id);
    2401          39 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2402             :                                            "mark_contract_paid",
    2403             :                                            params);
    2404          39 :   if (qs <= 0)
    2405           1 :     return qs;
    2406          38 :   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2407             :                                            "mark_inventory_sold",
    2408             :                                            uparams);
    2409          38 :   if (qs < 0)
    2410           0 :     return qs; /* 0: no inventory management, that's OK! */
    2411             :   /* ON DELETE CASCADE deletes from merchant_order_locks */
    2412          38 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2413             :                                              "delete_completed_order",
    2414             :                                              uparams);
    2415             : }
    2416             : 
    2417             : 
    2418             : /**
    2419             :  * Function called during aborts to refund a coin. Marks the
    2420             :  * respective coin as refunded.
    2421             :  *
    2422             :  * @param cls closure
    2423             :  * @param instance_id instance to refund payment for
    2424             :  * @param h_contract_terms hash of the contract to refund coin for
    2425             :  * @param refund_timestamp timestamp of the refund
    2426             :  * @param coin_pub public key of the coin to refund (fully)
    2427             :  * @param reason text justifying the refund
    2428             :  * @return transaction status
    2429             :  *        #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a coin_pub is unknown to us;
    2430             :  *        #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
    2431             :  *        regardless of whether it actually increased the refund
    2432             :  */
    2433             : static enum GNUNET_DB_QueryStatus
    2434          32 : postgres_refund_coin (void *cls,
    2435             :                       const char *instance_id,
    2436             :                       const struct GNUNET_HashCode *h_contract_terms,
    2437             :                       struct GNUNET_TIME_Absolute refund_timestamp,
    2438             :                       const struct TALER_CoinSpendPublicKeyP *coin_pub,
    2439             :                       const char *reason)
    2440             : {
    2441          32 :   struct PostgresClosure *pg = cls;
    2442          32 :   struct GNUNET_PQ_QueryParam params[] = {
    2443          32 :     GNUNET_PQ_query_param_string (instance_id),
    2444          32 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    2445          32 :     GNUNET_PQ_query_param_absolute_time (&refund_timestamp),
    2446          32 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    2447          32 :     GNUNET_PQ_query_param_string (reason),
    2448             :     GNUNET_PQ_query_param_end
    2449             :   };
    2450             : 
    2451          32 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2452             :                                              "refund_coin",
    2453             :                                              params);
    2454             : }
    2455             : 
    2456             : 
    2457             : /**
    2458             :  * Retrieve contract terms given its @a order_id
    2459             :  *
    2460             :  * @param cls closure
    2461             :  * @param instance_id instance's identifier
    2462             :  * @param order_id order to lookup contract for
    2463             :  * @param[out] h_contract_terms set to the hash of the contract.
    2464             :  * @param[out] paid set to the payment status of the contract
    2465             :  * @return transaction status
    2466             :  */
    2467             : static enum GNUNET_DB_QueryStatus
    2468          44 : postgres_lookup_order_status (void *cls,
    2469             :                               const char *instance_id,
    2470             :                               const char *order_id,
    2471             :                               struct GNUNET_HashCode *h_contract_terms,
    2472             :                               bool *paid)
    2473             : {
    2474          44 :   struct PostgresClosure *pg = cls;
    2475             :   uint8_t paid8;
    2476             :   enum GNUNET_DB_QueryStatus qs;
    2477          44 :   struct GNUNET_PQ_QueryParam params[] = {
    2478          44 :     GNUNET_PQ_query_param_string (instance_id),
    2479          44 :     GNUNET_PQ_query_param_string (order_id),
    2480             :     GNUNET_PQ_query_param_end
    2481             :   };
    2482          44 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2483          44 :     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    2484             :                                           h_contract_terms),
    2485          44 :     GNUNET_PQ_result_spec_auto_from_type ("paid",
    2486             :                                           &paid8),
    2487             :     GNUNET_PQ_result_spec_end
    2488             :   };
    2489             : 
    2490          44 :   check_connection (pg);
    2491          44 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2492             :                                                  "lookup_order_status",
    2493             :                                                  params,
    2494             :                                                  rs);
    2495          44 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    2496          42 :     *paid = (0 != paid8);
    2497             :   else
    2498           2 :     *paid = false; /* just to be safe(r) */
    2499          44 :   return qs;
    2500             : }
    2501             : 
    2502             : 
    2503             : /**
    2504             :  * Retrieve contract terms given its @a order_serial
    2505             :  *
    2506             :  * @param cls closure
    2507             :  * @param instance_id instance's identifier
    2508             :  * @param order_serial serial ID of the order to look up
    2509             :  * @param[out] order_id set to ID of the order
    2510             :  * @param[out] h_contract_terms set to the hash of the contract.
    2511             :  * @param[out] paid set to the payment status of the contract
    2512             :  * @return transaction status
    2513             :  */
    2514             : static enum GNUNET_DB_QueryStatus
    2515           4 : postgres_lookup_order_status_by_serial (void *cls,
    2516             :                                         const char *instance_id,
    2517             :                                         uint64_t order_serial,
    2518             :                                         char **order_id,
    2519             :                                         struct GNUNET_HashCode *h_contract_terms,
    2520             :                                         bool *paid)
    2521             : {
    2522           4 :   struct PostgresClosure *pg = cls;
    2523             :   uint8_t paid8;
    2524             :   enum GNUNET_DB_QueryStatus qs;
    2525           4 :   struct GNUNET_PQ_QueryParam params[] = {
    2526           4 :     GNUNET_PQ_query_param_string (instance_id),
    2527           4 :     GNUNET_PQ_query_param_uint64 (&order_serial),
    2528             :     GNUNET_PQ_query_param_end
    2529             :   };
    2530           4 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2531           4 :     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    2532             :                                           h_contract_terms),
    2533           4 :     GNUNET_PQ_result_spec_auto_from_type ("paid",
    2534             :                                           &paid8),
    2535           4 :     GNUNET_PQ_result_spec_string ("order_id",
    2536             :                                   order_id),
    2537             :     GNUNET_PQ_result_spec_end
    2538             :   };
    2539             : 
    2540           4 :   check_connection (pg);
    2541           4 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2542             :                                                  "lookup_order_status_by_serial",
    2543             :                                                  params,
    2544             :                                                  rs);
    2545           4 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    2546           4 :     *paid = (0 != paid8);
    2547             :   else
    2548           0 :     *paid = false; /* just to be safe(r) */
    2549           4 :   return qs;
    2550             : }
    2551             : 
    2552             : 
    2553             : /**
    2554             :  * Retrieve payment and wire status for a given @a order_serial and session ID.
    2555             :  *
    2556             :  * @param cls closure
    2557             :  * @param order_serial identifies the order
    2558             :  * @param session_id session for which to check the payment status, NULL for any
    2559             :  * @param[out] paid set to the payment status of the contract
    2560             :  * @param[out] wired set to the wire transfer status of the exchange payment
    2561             :  * @return transaction status
    2562             :  */
    2563             : static enum GNUNET_DB_QueryStatus
    2564          31 : postgres_lookup_payment_status (void *cls,
    2565             :                                 uint64_t order_serial,
    2566             :                                 const char *session_id,
    2567             :                                 bool *paid,
    2568             :                                 bool *wired)
    2569             : {
    2570          31 :   struct PostgresClosure *pg = cls;
    2571             :   uint8_t paid8;
    2572             :   uint8_t wired8;
    2573             :   enum GNUNET_DB_QueryStatus qs;
    2574          31 :   struct GNUNET_PQ_ResultSpec rs[] = {
    2575          31 :     GNUNET_PQ_result_spec_auto_from_type ("paid",
    2576             :                                           &paid8),
    2577          31 :     GNUNET_PQ_result_spec_auto_from_type ("wired",
    2578             :                                           &wired8),
    2579             :     GNUNET_PQ_result_spec_end
    2580             :   };
    2581          31 :   check_connection (pg);
    2582          31 :   if (NULL == session_id)
    2583             :   {
    2584          26 :     struct GNUNET_PQ_QueryParam params[] = {
    2585          26 :       GNUNET_PQ_query_param_uint64 (&order_serial),
    2586             :       GNUNET_PQ_query_param_end
    2587             :     };
    2588             : 
    2589          26 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2590             :                                                    "lookup_payment_status",
    2591             :                                                    params,
    2592             :                                                    rs);
    2593             :   }
    2594             :   else
    2595             :   {
    2596           5 :     struct GNUNET_PQ_QueryParam params[] = {
    2597           5 :       GNUNET_PQ_query_param_uint64 (&order_serial),
    2598           5 :       GNUNET_PQ_query_param_string (session_id),
    2599             :       GNUNET_PQ_query_param_end
    2600             :     };
    2601             : 
    2602           5 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    2603             :                                                    "lookup_payment_status_session_id",
    2604             :                                                    params,
    2605             :                                                    rs);
    2606             :   }
    2607          31 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    2608             :   {
    2609          27 :     *paid = (0 != paid8);
    2610          27 :     *wired = (0 != wired8);
    2611             :   }
    2612             :   else
    2613             :   {
    2614           4 :     *paid = false; /* just to be safe(r) */
    2615           4 :     *wired = false; /* just to be safe(r) */
    2616             :   }
    2617          31 :   return qs;
    2618             : }
    2619             : 
    2620             : 
    2621             : /**
    2622             :  * Closure for lookup_deposits_by_order_cb().
    2623             :  */
    2624             : struct LookupDepositsByOrderContext
    2625             : {
    2626             : 
    2627             :   /**
    2628             :    * Plugin context.
    2629             :    */
    2630             :   struct PostgresClosure *pg;
    2631             : 
    2632             :   /**
    2633             :    * Function to call with all results.
    2634             :    */
    2635             :   TALER_MERCHANTDB_DepositedCoinsCallback cb;
    2636             : 
    2637             :   /**
    2638             :    * Closure for @e cb.
    2639             :    */
    2640             :   void *cb_cls;
    2641             : 
    2642             :   /**
    2643             :    * Set to the query result.
    2644             :    */
    2645             :   enum GNUNET_DB_QueryStatus qs;
    2646             : };
    2647             : 
    2648             : 
    2649             : /**
    2650             :  * Function to be called with the results of a SELECT statement
    2651             :  * that has returned @a num_results results.
    2652             :  *
    2653             :  * @param cls of type `struct LookupDepositsByOrderContext *`
    2654             :  * @param result the postgres result
    2655             :  * @param num_results the number of results in @a result
    2656             :  */
    2657             : static void
    2658           8 : lookup_deposits_by_order_cb (void *cls,
    2659             :                              PGresult *result,
    2660             :                              unsigned int num_results)
    2661             : {
    2662           8 :   struct LookupDepositsByOrderContext *ldoc = cls;
    2663           8 :   struct PostgresClosure *pg = ldoc->pg;
    2664             : 
    2665          17 :   for (unsigned int i = 0; i<num_results; i++)
    2666             :   {
    2667             :     uint64_t deposit_serial;
    2668             :     char *exchange_url;
    2669             :     struct GNUNET_HashCode h_wire;
    2670             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    2671             :     struct TALER_Amount amount_with_fee;
    2672             :     struct TALER_Amount deposit_fee;
    2673           9 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2674           9 :       GNUNET_PQ_result_spec_uint64 ("deposit_serial",
    2675             :                                     &deposit_serial),
    2676           9 :       GNUNET_PQ_result_spec_string ("exchange_url",
    2677             :                                     &exchange_url),
    2678           9 :       GNUNET_PQ_result_spec_auto_from_type ("h_wire",
    2679             :                                             &h_wire),
    2680           9 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    2681             :                                    &amount_with_fee),
    2682           9 :       TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
    2683             :                                    &deposit_fee),
    2684           9 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    2685             :                                             &coin_pub),
    2686             :       GNUNET_PQ_result_spec_end
    2687             :     };
    2688             : 
    2689           9 :     if (GNUNET_OK !=
    2690           9 :         GNUNET_PQ_extract_result (result,
    2691             :                                   rs,
    2692             :                                   i))
    2693             :     {
    2694           0 :       GNUNET_break (0);
    2695           0 :       ldoc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2696           0 :       return;
    2697             :     }
    2698           9 :     ldoc->cb (ldoc->cb_cls,
    2699             :               deposit_serial,
    2700             :               exchange_url,
    2701             :               &h_wire,
    2702             :               &amount_with_fee,
    2703             :               &deposit_fee,
    2704             :               &coin_pub);
    2705           9 :     GNUNET_PQ_cleanup_result (rs); /* technically useless here */
    2706             :   }
    2707           8 :   ldoc->qs = num_results;
    2708             : }
    2709             : 
    2710             : 
    2711             : /**
    2712             :  * Retrieve details about coins that were deposited for an order.
    2713             :  *
    2714             :  * @param cls closure
    2715             :  * @param order_serial identifies the order
    2716             :  * @param cb function to call for each deposited coin
    2717             :  * @param cb_cls closure for @a cb
    2718             :  * @return transaction status
    2719             :  */
    2720             : static enum GNUNET_DB_QueryStatus
    2721           8 : postgres_lookup_deposits_by_order (void *cls,
    2722             :                                    uint64_t order_serial,
    2723             :                                    TALER_MERCHANTDB_DepositedCoinsCallback cb,
    2724             :                                    void *cb_cls)
    2725             : {
    2726           8 :   struct PostgresClosure *pg = cls;
    2727           8 :   struct LookupDepositsByOrderContext ldoc = {
    2728             :     .pg = pg,
    2729             :     .cb = cb,
    2730             :     .cb_cls = cb_cls
    2731             :   };
    2732           8 :   struct GNUNET_PQ_QueryParam params[] = {
    2733           8 :     GNUNET_PQ_query_param_uint64 (&order_serial),
    2734             :     GNUNET_PQ_query_param_end
    2735             :   };
    2736             :   enum GNUNET_DB_QueryStatus qs;
    2737             : 
    2738           8 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    2739             :                                              "lookup_deposits_by_order",
    2740             :                                              params,
    2741             :                                              &lookup_deposits_by_order_cb,
    2742             :                                              &ldoc);
    2743           8 :   if (qs < 0)
    2744           0 :     return qs;
    2745           8 :   return ldoc.qs;
    2746             : }
    2747             : 
    2748             : 
    2749             : /**
    2750             :  * Closure for lookup_deposits_by_order_cb().
    2751             :  */
    2752             : struct LookupTransferDetailsByOrderContext
    2753             : {
    2754             : 
    2755             :   /**
    2756             :    * Plugin context.
    2757             :    */
    2758             :   struct PostgresClosure *pg;
    2759             : 
    2760             :   /**
    2761             :    * Function to call with all results.
    2762             :    */
    2763             :   TALER_MERCHANTDB_OrderTransferDetailsCallback cb;
    2764             : 
    2765             :   /**
    2766             :    * Closure for @e cb.
    2767             :    */
    2768             :   void *cb_cls;
    2769             : 
    2770             :   /**
    2771             :    * Set to the query result.
    2772             :    */
    2773             :   enum GNUNET_DB_QueryStatus qs;
    2774             : };
    2775             : 
    2776             : 
    2777             : /**
    2778             :  * Function to be called with the results of a SELECT statement
    2779             :  * that has returned @a num_results results.
    2780             :  *
    2781             :  * @param cls of type `struct LookupTransferDetailsByOrderContext *`
    2782             :  * @param result the postgres result
    2783             :  * @param num_results the number of results in @a result
    2784             :  */
    2785             : static void
    2786          17 : lookup_transfer_details_by_order_cb (void *cls,
    2787             :                                      PGresult *result,
    2788             :                                      unsigned int num_results)
    2789             : {
    2790          17 :   struct LookupTransferDetailsByOrderContext *ltdo = cls;
    2791          17 :   struct PostgresClosure *pg = ltdo->pg;
    2792             : 
    2793          22 :   for (unsigned int i = 0; i<num_results; i++)
    2794             :   {
    2795             :     struct TALER_WireTransferIdentifierRawP wtid;
    2796             :     char *exchange_url;
    2797             :     uint64_t deposit_serial;
    2798             :     struct GNUNET_TIME_Absolute execution_time;
    2799             :     struct TALER_Amount deposit_value;
    2800             :     struct TALER_Amount deposit_fee;
    2801             :     uint8_t transfer_confirmed;
    2802           5 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2803           5 :       GNUNET_PQ_result_spec_uint64 ("deposit_serial",
    2804             :                                     &deposit_serial),
    2805           5 :       GNUNET_PQ_result_spec_absolute_time ("deposit_timestamp",
    2806             :                                            &execution_time),
    2807           5 :       GNUNET_PQ_result_spec_string ("exchange_url",
    2808             :                                     &exchange_url),
    2809           5 :       GNUNET_PQ_result_spec_auto_from_type ("wtid",
    2810             :                                             &wtid),
    2811           5 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
    2812             :                                    &deposit_value),
    2813           5 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
    2814             :                                    &deposit_fee),
    2815           5 :       GNUNET_PQ_result_spec_auto_from_type ("transfer_confirmed",
    2816             :                                             &transfer_confirmed),
    2817             :       GNUNET_PQ_result_spec_end
    2818             :     };
    2819             : 
    2820           5 :     if (GNUNET_OK !=
    2821           5 :         GNUNET_PQ_extract_result (result,
    2822             :                                   rs,
    2823             :                                   i))
    2824             :     {
    2825           0 :       GNUNET_break (0);
    2826           0 :       ltdo->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2827           0 :       return;
    2828             :     }
    2829           5 :     ltdo->cb (ltdo->cb_cls,
    2830             :               &wtid,
    2831             :               exchange_url,
    2832             :               execution_time,
    2833             :               &deposit_value,
    2834             :               &deposit_fee,
    2835             :               (0 != transfer_confirmed));
    2836           5 :     GNUNET_PQ_cleanup_result (rs); /* technically useless here */
    2837             :   }
    2838          17 :   ltdo->qs = num_results;
    2839             : }
    2840             : 
    2841             : 
    2842             : /**
    2843             :  * Retrieve wire transfer details for all deposits associated with
    2844             :  * a given @a order_serial.
    2845             :  *
    2846             :  * @param cls closure
    2847             :  * @param order_serial identifies the order
    2848             :  * @param cb function called with the wire transfer details
    2849             :  * @param cb_cls closure for @a cb
    2850             :  * @return transaction status
    2851             :  */
    2852             : static enum GNUNET_DB_QueryStatus
    2853          17 : postgres_lookup_transfer_details_by_order (
    2854             :   void *cls,
    2855             :   uint64_t order_serial,
    2856             :   TALER_MERCHANTDB_OrderTransferDetailsCallback cb,
    2857             :   void *cb_cls)
    2858             : {
    2859          17 :   struct PostgresClosure *pg = cls;
    2860          17 :   struct LookupTransferDetailsByOrderContext ltdo = {
    2861             :     .pg = pg,
    2862             :     .cb = cb,
    2863             :     .cb_cls = cb_cls
    2864             :   };
    2865          17 :   struct GNUNET_PQ_QueryParam params[] = {
    2866          17 :     GNUNET_PQ_query_param_uint64 (&order_serial),
    2867             :     GNUNET_PQ_query_param_end
    2868             :   };
    2869             :   enum GNUNET_DB_QueryStatus qs;
    2870             : 
    2871          17 :   qs = GNUNET_PQ_eval_prepared_multi_select (
    2872             :     pg->conn,
    2873             :     "lookup_transfer_details_by_order",
    2874             :     params,
    2875             :     &lookup_transfer_details_by_order_cb,
    2876             :     &ltdo);
    2877          17 :   if (qs < 0)
    2878           0 :     return qs;
    2879          17 :   return ltdo.qs;
    2880             : }
    2881             : 
    2882             : 
    2883             : /**
    2884             :  * Insert wire transfer details for a deposit.
    2885             :  *
    2886             :  * @param cls closure
    2887             :  * @param deposit_serial serial number of the deposit
    2888             :  * @param dd deposit transfer data from the exchange to store
    2889             :  * @return transaction status
    2890             :  */
    2891             : static enum GNUNET_DB_QueryStatus
    2892           3 : postgres_insert_deposit_to_transfer (
    2893             :   void *cls,
    2894             :   uint64_t deposit_serial,
    2895             :   const struct TALER_EXCHANGE_DepositData *dd)
    2896             : {
    2897           3 :   struct PostgresClosure *pg = cls;
    2898           3 :   struct GNUNET_PQ_QueryParam params[] = {
    2899           3 :     GNUNET_PQ_query_param_uint64 (&deposit_serial),
    2900           3 :     TALER_PQ_query_param_amount (&dd->coin_contribution),
    2901           3 :     GNUNET_PQ_query_param_absolute_time (&dd->execution_time),
    2902           3 :     GNUNET_PQ_query_param_auto_from_type (&dd->exchange_sig),
    2903           3 :     GNUNET_PQ_query_param_auto_from_type (&dd->exchange_pub),
    2904           3 :     GNUNET_PQ_query_param_auto_from_type (&dd->wtid),
    2905             :     GNUNET_PQ_query_param_end
    2906             :   };
    2907             : 
    2908           3 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2909             :                                              "insert_deposit_to_transfer",
    2910             :                                              params);
    2911             : }
    2912             : 
    2913             : 
    2914             : /**
    2915             :  * Set 'wired' status for an order to 'true'.
    2916             :  *
    2917             :  * @param cls closure
    2918             :  * @param order_serial serial number of the order
    2919             :  * @return transaction status
    2920             :  */
    2921             : static enum GNUNET_DB_QueryStatus
    2922          21 : postgres_mark_order_wired (void *cls,
    2923             :                            uint64_t order_serial)
    2924             : {
    2925          21 :   struct PostgresClosure *pg = cls;
    2926          21 :   struct GNUNET_PQ_QueryParam params[] = {
    2927          21 :     GNUNET_PQ_query_param_uint64 (&order_serial),
    2928             :     GNUNET_PQ_query_param_end
    2929             :   };
    2930             : 
    2931          21 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    2932             :                                              "mark_order_wired",
    2933             :                                              params);
    2934             : }
    2935             : 
    2936             : 
    2937             : /**
    2938             :  * Closure for #process_refund_cb().
    2939             :  */
    2940             : struct FindRefundContext
    2941             : {
    2942             : 
    2943             :   /**
    2944             :    * Plugin context.
    2945             :    */
    2946             :   struct PostgresClosure *pg;
    2947             : 
    2948             :   /**
    2949             :    * Updated to reflect total amount refunded so far.
    2950             :    */
    2951             :   struct TALER_Amount refunded_amount;
    2952             : 
    2953             :   /**
    2954             :    * Set to the largest refund transaction ID encountered.
    2955             :    */
    2956             :   uint64_t max_rtransaction_id;
    2957             : 
    2958             :   /**
    2959             :    * Set to true on hard errors.
    2960             :    */
    2961             :   bool err;
    2962             : };
    2963             : 
    2964             : 
    2965             : /**
    2966             :  * Function to be called with the results of a SELECT statement
    2967             :  * that has returned @a num_results results.
    2968             :  *
    2969             :  * @param cls closure, our `struct FindRefundContext`
    2970             :  * @param result the postgres result
    2971             :  * @param num_results the number of results in @a result
    2972             :  */
    2973             : static void
    2974          10 : process_refund_cb (void *cls,
    2975             :                    PGresult *result,
    2976             :                    unsigned int num_results)
    2977             : {
    2978          10 :   struct FindRefundContext *ictx = cls;
    2979          10 :   struct PostgresClosure *pg = ictx->pg;
    2980             : 
    2981          15 :   for (unsigned int i = 0; i<num_results; i++)
    2982             :   {
    2983             :     /* Sum up existing refunds */
    2984             :     struct TALER_Amount acc;
    2985             :     uint64_t rtransaction_id;
    2986           5 :     struct GNUNET_PQ_ResultSpec rs[] = {
    2987           5 :       TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
    2988             :                                    &acc),
    2989           5 :       GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
    2990             :                                     &rtransaction_id),
    2991             :       GNUNET_PQ_result_spec_end
    2992             :     };
    2993             : 
    2994           5 :     if (GNUNET_OK !=
    2995           5 :         GNUNET_PQ_extract_result (result,
    2996             :                                   rs,
    2997             :                                   i))
    2998             :     {
    2999           0 :       GNUNET_break (0);
    3000           0 :       ictx->err = true;
    3001           0 :       return;
    3002             :     }
    3003           5 :     if (0 >
    3004           5 :         TALER_amount_add (&ictx->refunded_amount,
    3005           5 :                           &ictx->refunded_amount,
    3006             :                           &acc))
    3007             :     {
    3008           0 :       GNUNET_break (0);
    3009           0 :       ictx->err = true;
    3010           0 :       return;
    3011             :     }
    3012           5 :     ictx->max_rtransaction_id = GNUNET_MAX (ictx->max_rtransaction_id,
    3013             :                                             rtransaction_id);
    3014           5 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3015             :                 "Found refund of %s\n",
    3016             :                 TALER_amount2s (&acc));
    3017             :   }
    3018             : }
    3019             : 
    3020             : 
    3021             : /**
    3022             :  * Closure for #process_deposits_for_refund_cb().
    3023             :  */
    3024             : struct InsertRefundContext
    3025             : {
    3026             :   /**
    3027             :    * Used to provide a connection to the db
    3028             :    */
    3029             :   struct PostgresClosure *pg;
    3030             : 
    3031             :   /**
    3032             :    * Amount to which increase the refund for this contract
    3033             :    */
    3034             :   const struct TALER_Amount *refund;
    3035             : 
    3036             :   /**
    3037             :    * Human-readable reason behind this refund
    3038             :    */
    3039             :   const char *reason;
    3040             : 
    3041             :   /**
    3042             :    * Transaction status code.
    3043             :    */
    3044             :   enum TALER_MERCHANTDB_RefundStatus rs;
    3045             : };
    3046             : 
    3047             : 
    3048             : /**
    3049             :  * Data extracted per coin.
    3050             :  */
    3051             : struct RefundCoinData
    3052             : {
    3053             : 
    3054             :   /**
    3055             :    * Public key of a coin.
    3056             :    */
    3057             :   struct TALER_CoinSpendPublicKeyP coin_pub;
    3058             : 
    3059             :   /**
    3060             :    * Amount deposited for this coin.
    3061             :    */
    3062             :   struct TALER_Amount deposited_with_fee;
    3063             : 
    3064             :   /**
    3065             :    * Amount refunded already for this coin.
    3066             :    */
    3067             :   struct TALER_Amount refund_amount;
    3068             : 
    3069             :   /**
    3070             :    * Order serial (actually not really per-coin).
    3071             :    */
    3072             :   uint64_t order_serial;
    3073             : 
    3074             :   /**
    3075             :    * Maximum rtransaction_id for this coin so far.
    3076             :    */
    3077             :   uint64_t max_rtransaction_id;
    3078             : 
    3079             : };
    3080             : 
    3081             : 
    3082             : /**
    3083             :  * Function to be called with the results of a SELECT statement
    3084             :  * that has returned @a num_results results.
    3085             :  *
    3086             :  * @param cls closure, our `struct InsertRefundContext`
    3087             :  * @param result the postgres result
    3088             :  * @param num_results the number of results in @a result
    3089             :  */
    3090             : static void
    3091           9 : process_deposits_for_refund_cb (void *cls,
    3092             :                                 PGresult *result,
    3093             :                                 unsigned int num_results)
    3094           9 : {
    3095           9 :   struct InsertRefundContext *ctx = cls;
    3096           9 :   struct PostgresClosure *pg = ctx->pg;
    3097             :   struct TALER_Amount current_refund;
    3098           9 :   struct RefundCoinData rcd[GNUNET_NZL (num_results)];
    3099             :   struct GNUNET_TIME_Absolute now;
    3100             : 
    3101           9 :   now = GNUNET_TIME_absolute_get ();
    3102           9 :   (void) GNUNET_TIME_round_abs (&now);
    3103           9 :   GNUNET_assert (GNUNET_OK ==
    3104             :                  TALER_amount_set_zero (ctx->refund->currency,
    3105             :                                         &current_refund));
    3106           9 :   memset (rcd, 0, sizeof (rcd));
    3107             :   /* Pass 1:  Collect amount of existing refunds into current_refund.
    3108             :    * Also store existing refunded amount for each deposit in deposit_refund. */
    3109          19 :   for (unsigned int i = 0; i<num_results; i++)
    3110             :   {
    3111          10 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3112          10 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    3113             :                                             &rcd[i].coin_pub),
    3114          10 :       GNUNET_PQ_result_spec_uint64 ("order_serial",
    3115             :                                     &rcd[i].order_serial),
    3116          10 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    3117             :                                    &rcd[i].deposited_with_fee),
    3118             :       GNUNET_PQ_result_spec_end
    3119             :     };
    3120          10 :     struct FindRefundContext ictx = {
    3121             :       .pg = pg
    3122             :     };
    3123             : 
    3124          10 :     if (GNUNET_OK !=
    3125          10 :         GNUNET_PQ_extract_result (result,
    3126             :                                   rs,
    3127             :                                   i))
    3128             :     {
    3129           0 :       GNUNET_break (0);
    3130           0 :       ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
    3131           0 :       return;
    3132             :     }
    3133             : 
    3134             :     {
    3135             :       enum GNUNET_DB_QueryStatus ires;
    3136          10 :       struct GNUNET_PQ_QueryParam params[] = {
    3137          10 :         GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
    3138          10 :         GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
    3139             :         GNUNET_PQ_query_param_end
    3140             :       };
    3141             : 
    3142          10 :       GNUNET_assert (GNUNET_OK ==
    3143             :                      TALER_amount_set_zero (ctx->refund->currency,
    3144             :                                             &ictx.refunded_amount));
    3145          10 :       ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn,
    3146             :                                                    "find_refunds_by_coin",
    3147             :                                                    params,
    3148             :                                                    &process_refund_cb,
    3149             :                                                    &ictx);
    3150          10 :       if ( (ictx.err) ||
    3151             :            (GNUNET_DB_STATUS_HARD_ERROR == ires) )
    3152             :       {
    3153           0 :         GNUNET_break (0);
    3154           0 :         ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
    3155           0 :         return;
    3156             :       }
    3157          10 :       if (GNUNET_DB_STATUS_SOFT_ERROR == ires)
    3158             :       {
    3159           0 :         ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
    3160           0 :         return;
    3161             :       }
    3162             :     }
    3163          10 :     if (0 >
    3164          10 :         TALER_amount_add (&current_refund,
    3165             :                           &current_refund,
    3166             :                           &ictx.refunded_amount))
    3167             :     {
    3168           0 :       GNUNET_break (0);
    3169           0 :       ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
    3170           0 :       return;
    3171             :     }
    3172          10 :     rcd[i].refund_amount = ictx.refunded_amount;
    3173          10 :     rcd[i].max_rtransaction_id = ictx.max_rtransaction_id;
    3174          10 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3175             :                 "Existing refund for coin %s is %s\n",
    3176             :                 TALER_B2S (&rcd[i].coin_pub),
    3177             :                 TALER_amount2s (&ictx.refunded_amount));
    3178             :   }
    3179             : 
    3180           9 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3181             :               "Total existing refund is %s\n",
    3182             :               TALER_amount2s (&current_refund));
    3183             : 
    3184             :   /* stop immediately if we are 'done' === amount already
    3185             :    * refunded.  */
    3186           9 :   if (0 >= TALER_amount_cmp (ctx->refund,
    3187             :                              &current_refund))
    3188             :   {
    3189           1 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3190             :                 "Existing refund of %s at or above requested refund. Finished early.\n",
    3191             :                 TALER_amount2s (&current_refund));
    3192           1 :     ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
    3193           1 :     return;
    3194             :   }
    3195             : 
    3196             :   /* Phase 2:  Try to increase current refund until it matches desired refund */
    3197          10 :   for (unsigned int i = 0; i<num_results; i++)
    3198             :   {
    3199             :     const struct TALER_Amount *increment;
    3200             :     struct TALER_Amount left;
    3201             :     struct TALER_Amount remaining_refund;
    3202             : 
    3203             :     /* How much of the coin is left after the existing refunds? */
    3204           8 :     if (0 >
    3205           8 :         TALER_amount_subtract (&left,
    3206           8 :                                &rcd[i].deposited_with_fee,
    3207           8 :                                &rcd[i].refund_amount))
    3208             :     {
    3209           0 :       GNUNET_break (0);
    3210           0 :       ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
    3211           6 :       return;
    3212             :     }
    3213             : 
    3214           8 :     if ( (0 == left.value) &&
    3215           1 :          (0 == left.fraction) )
    3216             :     {
    3217             :       /* coin was fully refunded, move to next coin */
    3218           1 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3219             :                   "Coin %s fully refunded, moving to next coin\n",
    3220             :                   TALER_B2S (&rcd[i].coin_pub));
    3221           1 :       continue;
    3222             :     }
    3223             : 
    3224           7 :     rcd[i].max_rtransaction_id++;
    3225             :     /* How much of the refund is still to be paid back? */
    3226           7 :     if (0 >
    3227           7 :         TALER_amount_subtract (&remaining_refund,
    3228             :                                ctx->refund,
    3229             :                                &current_refund))
    3230             :     {
    3231           0 :       GNUNET_break (0);
    3232           0 :       ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
    3233           0 :       return;
    3234             :     }
    3235             : 
    3236             :     /* By how much will we increase the refund for this coin? */
    3237           7 :     if (0 >= TALER_amount_cmp (&remaining_refund,
    3238             :                                &left))
    3239             :     {
    3240             :       /* remaining_refund <= left */
    3241           6 :       increment = &remaining_refund;
    3242             :     }
    3243             :     else
    3244             :     {
    3245           1 :       increment = &left;
    3246             :     }
    3247             : 
    3248           7 :     if (0 >
    3249           7 :         TALER_amount_add (&current_refund,
    3250             :                           &current_refund,
    3251             :                           increment))
    3252             :     {
    3253           0 :       GNUNET_break (0);
    3254           0 :       ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
    3255           0 :       return;
    3256             :     }
    3257             : 
    3258             :     /* actually run the refund */
    3259           7 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3260             :                 "Coin %s deposit amount is %s\n",
    3261             :                 TALER_B2S (&rcd[i].coin_pub),
    3262             :                 TALER_amount2s (&rcd[i].deposited_with_fee));
    3263           7 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3264             :                 "Coin %s refund will be incremented by %s\n",
    3265             :                 TALER_B2S (&rcd[i].coin_pub),
    3266             :                 TALER_amount2s (increment));
    3267             :     {
    3268             :       enum GNUNET_DB_QueryStatus qs;
    3269           7 :       struct GNUNET_PQ_QueryParam params[] = {
    3270           7 :         GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
    3271           7 :         GNUNET_PQ_query_param_uint64 (&rcd[i].max_rtransaction_id), /* already inc'ed */
    3272           7 :         GNUNET_PQ_query_param_absolute_time (&now),
    3273           7 :         GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
    3274           7 :         GNUNET_PQ_query_param_string (ctx->reason),
    3275           7 :         TALER_PQ_query_param_amount (increment),
    3276             :         GNUNET_PQ_query_param_end
    3277             :       };
    3278             : 
    3279           7 :       check_connection (pg);
    3280           7 :       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3281             :                                                "insert_refund",
    3282             :                                                params);
    3283           7 :       switch (qs)
    3284             :       {
    3285           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
    3286           0 :         GNUNET_break (0);
    3287           0 :         ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
    3288           0 :         return;
    3289           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
    3290           0 :         ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
    3291           0 :         return;
    3292           7 :       default:
    3293           7 :         ctx->rs = (enum TALER_MERCHANTDB_RefundStatus) qs;
    3294           7 :         break;
    3295             :       }
    3296             :     }
    3297             : 
    3298             :     /* stop immediately if we are done */
    3299           7 :     if (0 == TALER_amount_cmp (ctx->refund,
    3300             :                                &current_refund))
    3301             :     {
    3302           6 :       ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
    3303           6 :       return;
    3304             :     }
    3305             :   }
    3306             : 
    3307             :   /**
    3308             :    * We end up here if not all of the refund has been covered.
    3309             :    * Although this should be checked as the business should never
    3310             :    * issue a refund bigger than the contract's actual price, we cannot
    3311             :    * rely upon the frontend being correct.
    3312             :    *///
    3313           2 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    3314             :               "The refund of %s is bigger than the order's value\n",
    3315             :               TALER_amount2s (ctx->refund));
    3316           2 :   ctx->rs = TALER_MERCHANTDB_RS_TOO_HIGH;
    3317             : }
    3318             : 
    3319             : 
    3320             : /**
    3321             :  * Function called when some backoffice staff decides to award or
    3322             :  * increase the refund on an existing contract.  This function
    3323             :  * MUST be called from within a transaction scope setup by the
    3324             :  * caller as it executes multiple SQL statements.
    3325             :  *
    3326             :  * @param cls closure
    3327             :  * @param instance_id instance identifier
    3328             :  * @param order_id the order to increase the refund for
    3329             :  * @param refund maximum refund to return to the customer for this contract
    3330             :  * @param reason 0-terminated UTF-8 string giving the reason why the customer
    3331             :  *               got a refund (free form, business-specific)
    3332             :  * @return transaction status
    3333             :  *        #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a refund is ABOVE the amount we
    3334             :  *        were originally paid and thus the transaction failed;
    3335             :  *        #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
    3336             :  *        regardless of whether it actually increased the refund beyond
    3337             :  *        what was already refunded (idempotency!)
    3338             :  */
    3339             : static enum TALER_MERCHANTDB_RefundStatus
    3340           9 : postgres_increase_refund (void *cls,
    3341             :                           const char *instance_id,
    3342             :                           const char *order_id,
    3343             :                           const struct TALER_Amount *refund,
    3344             :                           const char *reason)
    3345             : {
    3346           9 :   struct PostgresClosure *pg = cls;
    3347             :   enum GNUNET_DB_QueryStatus qs;
    3348           9 :   struct GNUNET_PQ_QueryParam params[] = {
    3349           9 :     GNUNET_PQ_query_param_string (instance_id),
    3350           9 :     GNUNET_PQ_query_param_string (order_id),
    3351             :     GNUNET_PQ_query_param_end
    3352             :   };
    3353           9 :   struct InsertRefundContext ctx = {
    3354             :     .pg = pg,
    3355             :     .refund = refund,
    3356             :     .reason = reason
    3357             :   };
    3358             : 
    3359           9 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3360             :               "Asked to refund %s on order %s\n",
    3361             :               TALER_amount2s (refund),
    3362             :               order_id);
    3363           9 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    3364             :                                              "find_deposits_for_refund",
    3365             :                                              params,
    3366             :                                              &process_deposits_for_refund_cb,
    3367             :                                              &ctx);
    3368           9 :   switch (qs)
    3369             :   {
    3370           1 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    3371             :     /* never paid, means we clearly cannot refund anything */
    3372           1 :     return TALER_MERCHANTDB_RS_NO_SUCH_ORDER;
    3373           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
    3374           0 :     return TALER_MERCHANTDB_RS_SOFT_ERROR;
    3375           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    3376           0 :     return TALER_MERCHANTDB_RS_HARD_ERROR;
    3377           8 :   default:
    3378             :     /* Got one or more deposits */
    3379           8 :     return ctx.rs;
    3380             :   }
    3381             : }
    3382             : 
    3383             : 
    3384             : /**
    3385             :  * Closure for #lookup_refunds_detailed_cb().
    3386             :  */
    3387             : struct LookupRefundsDetailedContext
    3388             : {
    3389             :   /**
    3390             :    * Function to call for each refund.
    3391             :    */
    3392             :   TALER_MERCHANTDB_RefundDetailCallback rc;
    3393             : 
    3394             :   /**
    3395             :    * Closure for @e rc.
    3396             :    */
    3397             :   void *rc_cls;
    3398             : 
    3399             :   /**
    3400             :    * Plugin context.
    3401             :    */
    3402             :   struct PostgresClosure *pg;
    3403             : 
    3404             :   /**
    3405             :    * Transaction result.
    3406             :    */
    3407             :   enum GNUNET_DB_QueryStatus qs;
    3408             : };
    3409             : 
    3410             : 
    3411             : /**
    3412             :  * Function to be called with the results of a SELECT statement
    3413             :  * that has returned @a num_results results.
    3414             :  *
    3415             :  * @param cls of type `struct GetRefundsContext *`
    3416             :  * @param result the postgres result
    3417             :  * @param num_results the number of results in @a result
    3418             :  */
    3419             : static void
    3420          38 : lookup_refunds_detailed_cb (void *cls,
    3421             :                             PGresult *result,
    3422             :                             unsigned int num_results)
    3423             : {
    3424          38 :   struct LookupRefundsDetailedContext *lrdc = cls;
    3425          38 :   struct PostgresClosure *pg = lrdc->pg;
    3426             : 
    3427          62 :   for (unsigned int i = 0; i<num_results; i++)
    3428             :   {
    3429             :     uint64_t refund_serial;
    3430             :     struct GNUNET_TIME_Absolute timestamp;
    3431             :     struct TALER_CoinSpendPublicKeyP coin_pub;
    3432             :     uint64_t rtransaction_id;
    3433             :     struct TALER_Amount refund_amount;
    3434             :     char *reason;
    3435             :     char *exchange_url;
    3436             :     uint8_t pending8;
    3437          24 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3438          24 :       GNUNET_PQ_result_spec_uint64 ("refund_serial",
    3439             :                                     &refund_serial),
    3440          24 :       GNUNET_PQ_result_spec_absolute_time ("refund_timestamp",
    3441             :                                            &timestamp),
    3442          24 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    3443             :                                             &coin_pub),
    3444          24 :       GNUNET_PQ_result_spec_string ("exchange_url",
    3445             :                                     &exchange_url),
    3446          24 :       GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
    3447             :                                     &rtransaction_id),
    3448          24 :       GNUNET_PQ_result_spec_string ("reason",
    3449             :                                     &reason),
    3450          24 :       TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
    3451             :                                    &refund_amount),
    3452          24 :       GNUNET_PQ_result_spec_auto_from_type ("pending",
    3453             :                                             &pending8),
    3454             :       GNUNET_PQ_result_spec_end
    3455             :     };
    3456             : 
    3457          24 :     if (GNUNET_OK !=
    3458          24 :         GNUNET_PQ_extract_result (result,
    3459             :                                   rs,
    3460             :                                   i))
    3461             :     {
    3462           0 :       GNUNET_break (0);
    3463           0 :       lrdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    3464           0 :       return;
    3465             :     }
    3466          24 :     lrdc->rc (lrdc->rc_cls,
    3467             :               refund_serial,
    3468             :               timestamp,
    3469             :               &coin_pub,
    3470             :               exchange_url,
    3471             :               rtransaction_id,
    3472             :               reason,
    3473             :               &refund_amount,
    3474             :               0 != pending8);
    3475          24 :     GNUNET_PQ_cleanup_result (rs);
    3476             :   }
    3477          38 :   lrdc->qs = num_results;
    3478             : }
    3479             : 
    3480             : 
    3481             : /**
    3482             :  * Obtain detailed refund data associated with a contract.
    3483             :  *
    3484             :  * @param cls closure, typically a connection to the db
    3485             :  * @param instance_id instance to lookup refunds for
    3486             :  * @param h_contract_terms hash code of the contract
    3487             :  * @param rc function to call for each coin on which there is a refund
    3488             :  * @param rc_cls closure for @a rc
    3489             :  * @return transaction status
    3490             :  */
    3491             : static enum GNUNET_DB_QueryStatus
    3492          38 : postgres_lookup_refunds_detailed (
    3493             :   void *cls,
    3494             :   const char *instance_id,
    3495             :   const struct GNUNET_HashCode *h_contract_terms,
    3496             :   TALER_MERCHANTDB_RefundDetailCallback rc,
    3497             :   void *rc_cls)
    3498             : {
    3499          38 :   struct PostgresClosure *pg = cls;
    3500          38 :   struct GNUNET_PQ_QueryParam params[] = {
    3501          38 :     GNUNET_PQ_query_param_string (instance_id),
    3502          38 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    3503             :     GNUNET_PQ_query_param_end
    3504             :   };
    3505          38 :   struct LookupRefundsDetailedContext lrdc = {
    3506             :     .rc  = rc,
    3507             :     .rc_cls = rc_cls,
    3508             :     .pg = pg
    3509             :   };
    3510             :   enum GNUNET_DB_QueryStatus qs;
    3511             : 
    3512             :   /* no preflight check here, run in transaction by caller! */
    3513          38 :   TALER_LOG_DEBUG ("Looking for refund %s + %s\n",
    3514             :                    GNUNET_h2s (h_contract_terms),
    3515             :                    instance_id);
    3516          38 :   check_connection (pg);
    3517          38 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    3518             :                                              "lookup_refunds_detailed",
    3519             :                                              params,
    3520             :                                              &lookup_refunds_detailed_cb,
    3521             :                                              &lrdc);
    3522          38 :   if (0 >= qs)
    3523          22 :     return qs;
    3524          16 :   return lrdc.qs;
    3525             : }
    3526             : 
    3527             : 
    3528             : /**
    3529             :  * Insert refund proof data from the exchange into the database.
    3530             :  *
    3531             :  * @param cls closure
    3532             :  * @param refund_serial serial number of the refund
    3533             :  * @param exchange_sig signature from exchange that coin was refunded
    3534             :  * @param exchange_pub signing key that was used for @a exchange_sig
    3535             :  * @return transaction status
    3536             :  */
    3537             : static enum GNUNET_DB_QueryStatus
    3538           4 : postgres_insert_refund_proof (
    3539             :   void *cls,
    3540             :   uint64_t refund_serial,
    3541             :   const struct TALER_ExchangeSignatureP *exchange_sig,
    3542             :   const struct TALER_ExchangePublicKeyP *exchange_pub)
    3543             : {
    3544           4 :   struct PostgresClosure *pg = cls;
    3545           4 :   struct GNUNET_PQ_QueryParam params[] = {
    3546           4 :     GNUNET_PQ_query_param_uint64 (&refund_serial),
    3547           4 :     GNUNET_PQ_query_param_auto_from_type (exchange_sig),
    3548           4 :     GNUNET_PQ_query_param_auto_from_type (exchange_pub),
    3549             :     GNUNET_PQ_query_param_end
    3550             :   };
    3551             : 
    3552           4 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3553             :                                              "insert_refund_proof",
    3554             :                                              params);
    3555             : }
    3556             : 
    3557             : 
    3558             : /**
    3559             :  * Lookup refund proof data.
    3560             :  *
    3561             :  * @param cls closure
    3562             :  * @param refund_serial serial number of the refund
    3563             :  * @param[out] exchange_sig set to signature from exchange
    3564             :  * @param[out] exchange_pub signing key that was used for @a exchange_sig
    3565             :  * @return transaction status
    3566             :  */
    3567             : static enum GNUNET_DB_QueryStatus
    3568           5 : postgres_lookup_refund_proof (void *cls,
    3569             :                               uint64_t refund_serial,
    3570             :                               struct TALER_ExchangeSignatureP *exchange_sig,
    3571             :                               struct TALER_ExchangePublicKeyP *exchange_pub)
    3572             : {
    3573           5 :   struct PostgresClosure *pg = cls;
    3574           5 :   struct GNUNET_PQ_QueryParam params[] = {
    3575           5 :     GNUNET_PQ_query_param_uint64 (&refund_serial),
    3576             :     GNUNET_PQ_query_param_end
    3577             :   };
    3578           5 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3579           5 :     GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
    3580             :                                           exchange_sig),
    3581           5 :     GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
    3582             :                                           exchange_pub),
    3583             :     GNUNET_PQ_result_spec_end
    3584             :   };
    3585             : 
    3586           5 :   check_connection (pg);
    3587           5 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3588             :                                                    "lookup_refund_proof",
    3589             :                                                    params,
    3590             :                                                    rs);
    3591             : }
    3592             : 
    3593             : 
    3594             : /**
    3595             :  * Retrieve the order ID that was used to pay for a resource within a session.
    3596             :  *
    3597             :  * @param cls closure
    3598             :  * @param instance_id identifying the instance
    3599             :  * @param fulfillment_url URL that canonically identifies the resource
    3600             :  *        being paid for
    3601             :  * @param session_id session id
    3602             :  * @param[out] order_id where to store the order ID that was used when
    3603             :  *             paying for the resource URL
    3604             :  * @return transaction status
    3605             :  */
    3606             : enum GNUNET_DB_QueryStatus
    3607          25 : postgres_lookup_order_by_fulfillment (void *cls,
    3608             :                                       const char *instance_id,
    3609             :                                       const char *fulfillment_url,
    3610             :                                       const char *session_id,
    3611             :                                       char **order_id)
    3612             : {
    3613          25 :   struct PostgresClosure *pg = cls;
    3614          25 :   struct GNUNET_PQ_QueryParam params[] = {
    3615          25 :     GNUNET_PQ_query_param_string (instance_id),
    3616          25 :     GNUNET_PQ_query_param_string (fulfillment_url),
    3617          25 :     GNUNET_PQ_query_param_string (session_id),
    3618             :     GNUNET_PQ_query_param_end
    3619             :   };
    3620          25 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3621          25 :     GNUNET_PQ_result_spec_string ("order_id",
    3622             :                                   order_id),
    3623             :     GNUNET_PQ_result_spec_end
    3624             :   };
    3625             : 
    3626          25 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3627             :                                                    "lookup_order_by_fulfillment",
    3628             :                                                    params,
    3629             :                                                    rs);
    3630             : }
    3631             : 
    3632             : 
    3633             : /**
    3634             :  * Insert information about a wire transfer the merchant has received.
    3635             :  *
    3636             :  * @param cls closure
    3637             :  * @param instance_id the instance that received the transfer
    3638             :  * @param exchange_url which exchange made the transfer
    3639             :  * @param wtid identifier of the wire transfer
    3640             :  * @param credit_amount how much did we receive
    3641             :  * @param payto_uri what is the merchant's bank account that received the transfer
    3642             :  * @param confirmed whether the transfer was confirmed by the merchant or
    3643             :  *                  was merely claimed by the exchange at this point
    3644             :  * @return transaction status
    3645             :  */
    3646             : static enum GNUNET_DB_QueryStatus
    3647           4 : postgres_insert_transfer (
    3648             :   void *cls,
    3649             :   const char *instance_id,
    3650             :   const char *exchange_url,
    3651             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    3652             :   const struct TALER_Amount *credit_amount,
    3653             :   const char *payto_uri,
    3654             :   bool confirmed)
    3655             : {
    3656           4 :   struct PostgresClosure *pg = cls;
    3657           4 :   uint8_t confirmed8 = confirmed;
    3658           4 :   struct GNUNET_PQ_QueryParam params[] = {
    3659           4 :     GNUNET_PQ_query_param_string (exchange_url),
    3660           4 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    3661           4 :     TALER_PQ_query_param_amount (credit_amount),
    3662           4 :     GNUNET_PQ_query_param_string (payto_uri),
    3663           4 :     GNUNET_PQ_query_param_auto_from_type (&confirmed8),
    3664           4 :     GNUNET_PQ_query_param_string (instance_id),
    3665             :     GNUNET_PQ_query_param_end
    3666             :   };
    3667             : 
    3668           4 :   check_connection (pg);
    3669           4 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3670             :                                              "insert_transfer",
    3671             :                                              params);
    3672             : }
    3673             : 
    3674             : 
    3675             : /**
    3676             :  * Delete information about a transfer. Note that transfers
    3677             :  * confirmed by the exchange cannot be deleted anymore.
    3678             :  *
    3679             :  * @param cls closure
    3680             :  * @param instance_id instance to delete transfer of
    3681             :  * @param transfer_serial_id transfer to delete
    3682             :  * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
    3683             :  *           if deletion is prohibited OR transfer is unknown
    3684             :  */
    3685             : static enum GNUNET_DB_QueryStatus
    3686           1 : postgres_delete_transfer (void *cls,
    3687             :                           const char *instance_id,
    3688             :                           uint64_t transfer_serial_id)
    3689             : {
    3690           1 :   struct PostgresClosure *pg = cls;
    3691           1 :   struct GNUNET_PQ_QueryParam params[] = {
    3692           1 :     GNUNET_PQ_query_param_string (instance_id),
    3693           1 :     GNUNET_PQ_query_param_uint64 (&transfer_serial_id),
    3694             :     GNUNET_PQ_query_param_end
    3695             :   };
    3696             : 
    3697           1 :   check_connection (pg);
    3698           1 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3699             :                                              "delete_transfer",
    3700             :                                              params);
    3701             : }
    3702             : 
    3703             : 
    3704             : /**
    3705             :  * Check if information about a transfer exists with the
    3706             :  * backend.  Returns no data, only the query status.
    3707             :  *
    3708             :  * @param cls closure
    3709             :  * @param instance_id instance to delete transfer of
    3710             :  * @param transfer_serial_id transfer to delete
    3711             :  * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    3712             :  *           if the transfer record exists
    3713             :  */
    3714             : static enum GNUNET_DB_QueryStatus
    3715           0 : postgres_check_transfer_exists (void *cls,
    3716             :                                 const char *instance_id,
    3717             :                                 uint64_t transfer_serial_id)
    3718             : {
    3719           0 :   struct PostgresClosure *pg = cls;
    3720           0 :   struct GNUNET_PQ_QueryParam params[] = {
    3721           0 :     GNUNET_PQ_query_param_string (instance_id),
    3722           0 :     GNUNET_PQ_query_param_uint64 (&transfer_serial_id),
    3723             :     GNUNET_PQ_query_param_end
    3724             :   };
    3725           0 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3726             :     GNUNET_PQ_result_spec_end
    3727             :   };
    3728             : 
    3729           0 :   check_connection (pg);
    3730           0 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3731             :                                                    "check_transfer_exists",
    3732             :                                                    params,
    3733             :                                                    rs);
    3734             : }
    3735             : 
    3736             : 
    3737             : /**
    3738             :  * Lookup account serial by payto URI.
    3739             :  *
    3740             :  * @param cls closure
    3741             :  * @param instance_id instance to lookup the account from
    3742             :  * @param payto_uri what is the merchant's bank account to lookup
    3743             :  * @param[out] account_serial serial number of the account
    3744             :  * @return transaction status
    3745             :  */
    3746             : static enum GNUNET_DB_QueryStatus
    3747           4 : postgres_lookup_account (void *cls,
    3748             :                          const char *instance_id,
    3749             :                          const char *payto_uri,
    3750             :                          uint64_t *account_serial)
    3751             : {
    3752           4 :   struct PostgresClosure *pg = cls;
    3753           4 :   struct GNUNET_PQ_QueryParam params[] = {
    3754           4 :     GNUNET_PQ_query_param_string (instance_id),
    3755           4 :     GNUNET_PQ_query_param_string (payto_uri),
    3756             :     GNUNET_PQ_query_param_end
    3757             :   };
    3758           4 :   struct GNUNET_PQ_ResultSpec rs[] = {
    3759           4 :     GNUNET_PQ_result_spec_uint64 ("account_serial",
    3760             :                                   account_serial),
    3761             :     GNUNET_PQ_result_spec_end
    3762             :   };
    3763             : 
    3764           4 :   check_connection (pg);
    3765           4 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3766             :                                                    "lookup_account",
    3767             :                                                    params,
    3768             :                                                    rs);
    3769             : }
    3770             : 
    3771             : 
    3772             : /**
    3773             :  * Insert information about a wire transfer the merchant has received.
    3774             :  *
    3775             :  * @param cls closure
    3776             :  * @param instance_id instance to provide transfer details for
    3777             :  * @param exchange_url which exchange made the transfer
    3778             :  * @param payto_uri what is the merchant's bank account that received the transfer
    3779             :  * @param wtid identifier of the wire transfer
    3780             :  * @param td transfer details to store
    3781             :  * @return transaction status,
    3782             :  *   #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the @a wtid and @a exchange_uri are not known for this @a instance_id
    3783             :  *   #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
    3784             :  */
    3785             : static enum GNUNET_DB_QueryStatus
    3786           2 : postgres_insert_transfer_details (
    3787             :   void *cls,
    3788             :   const char *instance_id,
    3789             :   const char *exchange_url,
    3790             :   const char *payto_uri,
    3791             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    3792             :   const struct TALER_EXCHANGE_TransferData *td)
    3793             : {
    3794           2 :   struct PostgresClosure *pg = cls;
    3795             :   enum GNUNET_DB_QueryStatus qs;
    3796             :   uint64_t credit_serial;
    3797             :   unsigned int retries;
    3798             : 
    3799           2 :   retries = 0;
    3800           2 :   check_connection (pg);
    3801           2 : RETRY:
    3802           2 :   if (MAX_RETRIES < ++retries)
    3803           0 :     return GNUNET_DB_STATUS_SOFT_ERROR;
    3804           2 :   if (GNUNET_OK !=
    3805           2 :       postgres_start_read_committed (pg,
    3806             :                                      "insert transfer details"))
    3807             :   {
    3808           0 :     GNUNET_break (0);
    3809           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    3810             :   }
    3811             : 
    3812             :   /* lookup credit serial */
    3813             :   {
    3814           2 :     struct GNUNET_PQ_QueryParam params[] = {
    3815           2 :       GNUNET_PQ_query_param_string (exchange_url),
    3816           2 :       GNUNET_PQ_query_param_string (payto_uri),
    3817           2 :       GNUNET_PQ_query_param_string (instance_id),
    3818           2 :       GNUNET_PQ_query_param_auto_from_type (wtid),
    3819             :       GNUNET_PQ_query_param_end
    3820             :     };
    3821           2 :     struct GNUNET_PQ_ResultSpec rs[] = {
    3822           2 :       GNUNET_PQ_result_spec_uint64 ("credit_serial",
    3823             :                                     &credit_serial),
    3824             :       GNUNET_PQ_result_spec_end
    3825             :     };
    3826             : 
    3827           2 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    3828             :                                                    "lookup_credit_serial",
    3829             :                                                    params,
    3830             :                                                    rs);
    3831           2 :     if (0 > qs)
    3832             :     {
    3833           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3834           0 :       postgres_rollback (pg);
    3835           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3836           0 :         goto RETRY;
    3837           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3838             :                   "'lookup_credit_serial' for account %s and amount %s failed with status %d\n",
    3839             :                   payto_uri,
    3840             :                   TALER_amount2s (&td->total_amount),
    3841             :                   qs);
    3842           0 :       return qs;
    3843             :     }
    3844           2 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    3845             :     {
    3846           0 :       postgres_rollback (pg);
    3847           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3848             :                   "'lookup_credit_serial' for account %s failed with transfer unknown\n",
    3849             :                   payto_uri);
    3850           0 :       return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    3851             :     }
    3852             :   }
    3853             : 
    3854             :   /* update merchant_transfer_signatures table */
    3855             :   {
    3856           2 :     struct GNUNET_PQ_QueryParam params[] = {
    3857           2 :       GNUNET_PQ_query_param_uint64 (&credit_serial),
    3858           2 :       TALER_PQ_query_param_amount (&td->total_amount),
    3859           2 :       TALER_PQ_query_param_amount (&td->wire_fee),
    3860           2 :       GNUNET_PQ_query_param_absolute_time (&td->execution_time),
    3861           2 :       GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
    3862           2 :       GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
    3863             :       GNUNET_PQ_query_param_end
    3864             :     };
    3865             : 
    3866           2 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3867             :                                              "insert_transfer_signature",
    3868             :                                              params);
    3869           2 :     if (0 > qs)
    3870             :     {
    3871             :       /* FIXME: distinguish hard error */
    3872           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3873           0 :       postgres_rollback (pg);
    3874           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3875           0 :         goto RETRY;
    3876           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3877             :                   "'insert_transfer_signature' failed with status %d\n",
    3878             :                   qs);
    3879           0 :       return qs;
    3880             :     }
    3881           2 :     if (0 == qs)
    3882             :     {
    3883           0 :       postgres_rollback (pg);
    3884           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3885             :                   "'insert_transfer_signature' failed with status %d\n",
    3886             :                   qs);
    3887           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    3888             :     }
    3889             :   }
    3890             : 
    3891             :   /* Update transfer-coin association table */
    3892           2 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3893             :               "Updating transfer-coin association table\n");
    3894           4 :   for (unsigned int i = 0; i<td->details_length; i++)
    3895             :   {
    3896           2 :     const struct TALER_TrackTransferDetails *d = &td->details[i];
    3897           2 :     uint64_t i64 = (uint64_t) i;
    3898           2 :     struct GNUNET_PQ_QueryParam params[] = {
    3899           2 :       GNUNET_PQ_query_param_uint64 (&credit_serial),
    3900           2 :       GNUNET_PQ_query_param_uint64 (&i64),
    3901           2 :       TALER_PQ_query_param_amount (&d->coin_value),
    3902           2 :       TALER_PQ_query_param_amount (&d->coin_fee), /* deposit fee */
    3903           2 :       GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
    3904           2 :       GNUNET_PQ_query_param_auto_from_type (&d->h_contract_terms),
    3905             :       GNUNET_PQ_query_param_end
    3906             :     };
    3907             : 
    3908           2 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3909             :                                              "insert_transfer_to_coin_mapping",
    3910             :                                              params);
    3911           2 :     if (0 > qs)
    3912             :     {
    3913             :       /* FIXME: distinguish hard error */
    3914           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3915           0 :       postgres_rollback (pg);
    3916           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3917           0 :         goto RETRY;
    3918           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3919             :                   "'insert_transfer_to_coin_mapping' failed with status %d\n",
    3920             :                   qs);
    3921           0 :       return qs;
    3922             :     }
    3923             :   }
    3924             :   /* Update merchant_contract_terms 'wired' status: for all coins
    3925             :      that were wired, set the respective order's "wired" status to
    3926             :      true, *if* all other deposited coins associated with that order
    3927             :      have also been wired (this time or earlier) */
    3928           2 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3929             :               "Updating contract terms 'wired' status\n");
    3930           4 :   for (unsigned int i = 0; i<td->details_length; i++)
    3931             :   {
    3932           2 :     const struct TALER_TrackTransferDetails *d = &td->details[i];
    3933           2 :     struct GNUNET_PQ_QueryParam params[] = {
    3934           2 :       GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
    3935             :       GNUNET_PQ_query_param_end
    3936             :     };
    3937             : 
    3938           2 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    3939             :                                              "update_wired_by_coin_pub",
    3940             :                                              params);
    3941           2 :     if (0 > qs)
    3942             :     {
    3943             :       /* FIXME: distinguish hard error */
    3944           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3945           0 :       postgres_rollback (pg);
    3946           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3947           0 :         goto RETRY;
    3948           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3949             :                   "'update_wired_by_coin_pub' failed with status %d\n",
    3950             :                   qs);
    3951           0 :       return qs;
    3952             :     }
    3953             :   }
    3954           2 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3955             :               "Committing transaction...\n");
    3956           2 :   qs = postgres_commit (pg);
    3957           2 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    3958           2 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    3959           0 :   GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    3960           0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    3961           0 :     goto RETRY;
    3962           0 :   return qs;
    3963             : }
    3964             : 
    3965             : 
    3966             : /**
    3967             :  * Obtain information about wire fees charged by an exchange,
    3968             :  * including signature (so we have proof).
    3969             :  *
    3970             :  * @param cls closure
    3971             :  * @param master_pub public key of the exchange
    3972             :  * @param wire_method the wire method
    3973             :  * @param contract_date date of the contract to use for the lookup
    3974             :  * @param[out] wire_fee wire fee charged
    3975             :  * @param[out] closing_fee closing fee charged (irrelevant for us,
    3976             :  *              but needed to check signature)
    3977             :  * @param[out] start_date start of fee being used
    3978             :  * @param[out] end_date end of fee being used
    3979             :  * @param[out] master_sig signature of exchange over fee structure
    3980             :  * @return transaction status code
    3981             :  */
    3982             : static enum GNUNET_DB_QueryStatus
    3983           4 : postgres_lookup_wire_fee (void *cls,
    3984             :                           const struct TALER_MasterPublicKeyP *master_pub,
    3985             :                           const char *wire_method,
    3986             :                           struct GNUNET_TIME_Absolute contract_date,
    3987             :                           struct TALER_Amount *wire_fee,
    3988             :                           struct TALER_Amount *closing_fee,
    3989             :                           struct GNUNET_TIME_Absolute *start_date,
    3990             :                           struct GNUNET_TIME_Absolute *end_date,
    3991             :                           struct TALER_MasterSignatureP *master_sig)
    3992             : {
    3993           4 :   struct PostgresClosure *pg = cls;
    3994             :   struct GNUNET_HashCode h_wire_method;
    3995           4 :   struct GNUNET_PQ_QueryParam params[] = {
    3996           4 :     GNUNET_PQ_query_param_auto_from_type (master_pub),
    3997           4 :     GNUNET_PQ_query_param_auto_from_type (&h_wire_method),
    3998           4 :     GNUNET_PQ_query_param_absolute_time (&contract_date),
    3999             :     GNUNET_PQ_query_param_end
    4000             :   };
    4001           4 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4002           4 :     TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
    4003             :                                  wire_fee),
    4004           4 :     TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
    4005             :                                  closing_fee),
    4006           4 :     GNUNET_PQ_result_spec_absolute_time ("start_date",
    4007             :                                          start_date),
    4008           4 :     GNUNET_PQ_result_spec_absolute_time ("end_date",
    4009             :                                          end_date),
    4010           4 :     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
    4011             :                                           master_sig),
    4012             :     GNUNET_PQ_result_spec_end
    4013             :   };
    4014             : 
    4015           4 :   check_connection (pg);
    4016           4 :   GNUNET_CRYPTO_hash (wire_method,
    4017           4 :                       strlen (wire_method) + 1,
    4018             :                       &h_wire_method);
    4019           4 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4020             :                                                    "lookup_wire_fee",
    4021             :                                                    params,
    4022             :                                                    rs);
    4023             : }
    4024             : 
    4025             : 
    4026             : /**
    4027             :  * Closure for #lookup_deposits_by_contract_and_coin_cb().
    4028             :  */
    4029             : struct LookupDepositsByCnCContext
    4030             : {
    4031             :   /**
    4032             :    * Function to call for each deposit.
    4033             :    */
    4034             :   TALER_MERCHANTDB_CoinDepositCallback cb;
    4035             : 
    4036             :   /**
    4037             :    * Closure for @e cb.
    4038             :    */
    4039             :   void *cb_cls;
    4040             : 
    4041             :   /**
    4042             :    * Plugin context.
    4043             :    */
    4044             :   struct PostgresClosure *pg;
    4045             : 
    4046             :   /**
    4047             :    * Transaction result.
    4048             :    */
    4049             :   enum GNUNET_DB_QueryStatus qs;
    4050             : };
    4051             : 
    4052             : 
    4053             : /**
    4054             :  * Function to be called with the results of a SELECT statement
    4055             :  * that has returned @a num_results results.
    4056             :  *
    4057             :  * @param cls of type `struct LookupDepositsByCnCContext *`
    4058             :  * @param result the postgres result
    4059             :  * @param num_results the number of results in @a result
    4060             :  */
    4061             : static void
    4062           2 : lookup_deposits_by_contract_and_coin_cb (void *cls,
    4063             :                                          PGresult *result,
    4064             :                                          unsigned int num_results)
    4065             : {
    4066           2 :   struct LookupDepositsByCnCContext *ldcc = cls;
    4067           2 :   struct PostgresClosure *pg = ldcc->pg;
    4068             : 
    4069           4 :   for (unsigned int i = 0; i<num_results; i++)
    4070             :   {
    4071             :     char *exchange_url;
    4072             :     struct TALER_Amount amount_with_fee;
    4073             :     struct TALER_Amount deposit_fee;
    4074             :     struct TALER_Amount refund_fee;
    4075             :     struct TALER_Amount wire_fee;
    4076             :     struct GNUNET_HashCode h_wire;
    4077             :     struct GNUNET_TIME_Absolute deposit_timestamp;
    4078             :     struct GNUNET_TIME_Absolute refund_deadline;
    4079             :     struct TALER_ExchangeSignatureP exchange_sig;
    4080             :     struct TALER_ExchangePublicKeyP exchange_pub;
    4081           2 :     struct GNUNET_PQ_ResultSpec rs[] = {
    4082           2 :       GNUNET_PQ_result_spec_string ("exchange_url",
    4083             :                                     &exchange_url),
    4084           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
    4085             :                                    &amount_with_fee),
    4086           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
    4087             :                                    &deposit_fee),
    4088           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
    4089             :                                    &refund_fee),
    4090           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
    4091             :                                    &wire_fee),
    4092           2 :       GNUNET_PQ_result_spec_auto_from_type ("h_wire",
    4093             :                                             &h_wire),
    4094           2 :       GNUNET_PQ_result_spec_absolute_time ("deposit_timestamp",
    4095             :                                            &deposit_timestamp),
    4096           2 :       GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
    4097             :                                            &refund_deadline),
    4098           2 :       GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
    4099             :                                             &exchange_sig),
    4100           2 :       GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
    4101             :                                             &exchange_pub),
    4102             :       GNUNET_PQ_result_spec_end
    4103             :     };
    4104             : 
    4105           2 :     if (GNUNET_OK !=
    4106           2 :         GNUNET_PQ_extract_result (result,
    4107             :                                   rs,
    4108             :                                   i))
    4109             :     {
    4110           0 :       GNUNET_break (0);
    4111           0 :       ldcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    4112           0 :       return;
    4113             :     }
    4114           2 :     ldcc->cb (ldcc->cb_cls,
    4115             :               exchange_url,
    4116             :               &amount_with_fee,
    4117             :               &deposit_fee,
    4118             :               &refund_fee,
    4119             :               &wire_fee,
    4120             :               &h_wire,
    4121             :               deposit_timestamp,
    4122             :               refund_deadline,
    4123             :               &exchange_sig,
    4124             :               &exchange_pub);
    4125           2 :     GNUNET_PQ_cleanup_result (rs);
    4126             :   }
    4127           2 :   ldcc->qs = num_results;
    4128             : }
    4129             : 
    4130             : 
    4131             : /**
    4132             :  * Lookup information about coin payments by @a h_contract_terms and
    4133             :  * @a coin_pub.
    4134             :  *
    4135             :  * @param cls closure
    4136             :  * @param instance_id instance to lookup payments for
    4137             :  * @param h_contract_terms proposal data's hashcode
    4138             :  * @param coin_pub public key to use for the search
    4139             :  * @param cb function to call with payment data
    4140             :  * @param cb_cls closure for @a cb
    4141             :  * @return transaction status
    4142             :  */
    4143             : static enum GNUNET_DB_QueryStatus
    4144           2 : postgres_lookup_deposits_by_contract_and_coin (
    4145             :   void *cls,
    4146             :   const char *instance_id,
    4147             :   const struct GNUNET_HashCode *h_contract_terms,
    4148             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    4149             :   TALER_MERCHANTDB_CoinDepositCallback cb,
    4150             :   void *cb_cls)
    4151             : {
    4152           2 :   struct PostgresClosure *pg = cls;
    4153           2 :   struct GNUNET_PQ_QueryParam params[] = {
    4154           2 :     GNUNET_PQ_query_param_string (instance_id),
    4155           2 :     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
    4156           2 :     GNUNET_PQ_query_param_auto_from_type (coin_pub),
    4157             :     GNUNET_PQ_query_param_end
    4158             :   };
    4159           2 :   struct LookupDepositsByCnCContext ldcc = {
    4160             :     .cb  = cb,
    4161             :     .cb_cls = cb_cls,
    4162             :     .pg = pg
    4163             :   };
    4164             :   enum GNUNET_DB_QueryStatus qs;
    4165             : 
    4166           2 :   check_connection (pg);
    4167           2 :   qs = GNUNET_PQ_eval_prepared_multi_select (
    4168             :     pg->conn,
    4169             :     "lookup_deposits_by_contract_and_coin",
    4170             :     params,
    4171             :     &lookup_deposits_by_contract_and_coin_cb,
    4172             :     &ldcc);
    4173           2 :   if (0 >= qs)
    4174           0 :     return qs;
    4175           2 :   return ldcc.qs;
    4176             : }
    4177             : 
    4178             : 
    4179             : /**
    4180             :  * Lookup transfer status.
    4181             :  *
    4182             :  * @param cls closure
    4183             :  * @param instance_id at which instance should we resolve the transfer
    4184             :  * @param exchange_url the exchange that made the transfer
    4185             :  * @param wtid wire transfer subject
    4186             :  * @param[out] total_amount amount that was debited from our
    4187             :  *             aggregate balance at the exchange (in total, sum of
    4188             :  *             the wire transfer amount and the @a wire_fee)
    4189             :  * @param[out] wire_fee the wire fee the exchange charged (only set if @a have_exchange_sig is true)
    4190             :  * @param[out] exchange_amount the amount the exchange claims was transferred (only set if @a have_exchange_sig is true)
    4191             :  * @param[out] execution_time when the transfer was executed by the exchange (only set if @a have_exchange_sig is true)
    4192             :  * @param[out] have_exchange_sig do we have a response from the exchange about this transfer
    4193             :  * @param[out] verified did we confirm the transfer was OK
    4194             :  * @return transaction status
    4195             :  */
    4196             : static enum GNUNET_DB_QueryStatus
    4197           5 : postgres_lookup_transfer (
    4198             :   void *cls,
    4199             :   const char *instance_id,
    4200             :   const char *exchange_url,
    4201             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    4202             :   struct TALER_Amount *total_amount,
    4203             :   struct TALER_Amount *wire_fee,
    4204             :   struct TALER_Amount *exchange_amount,
    4205             :   struct GNUNET_TIME_Absolute *execution_time,
    4206             :   bool *have_exchange_sig,
    4207             :   bool *verified)
    4208             : {
    4209           5 :   struct PostgresClosure *pg = cls;
    4210           5 :   struct GNUNET_PQ_QueryParam params[] = {
    4211           5 :     GNUNET_PQ_query_param_string (exchange_url),
    4212           5 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    4213           5 :     GNUNET_PQ_query_param_string (instance_id),
    4214             :     GNUNET_PQ_query_param_end
    4215             :   };
    4216             :   uint8_t verified8;
    4217             :   /** Amount we got actually credited, _excludes_ the wire fee */
    4218             :   bool no_sig;
    4219             :   struct TALER_Amount credit_amount;
    4220           5 :   struct GNUNET_PQ_ResultSpec rs[] = {
    4221           5 :     TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount",
    4222             :                                  &credit_amount),
    4223           5 :     GNUNET_PQ_result_spec_allow_null (
    4224           5 :       TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
    4225             :                                    wire_fee),
    4226             :       &no_sig),
    4227           5 :     GNUNET_PQ_result_spec_allow_null (
    4228           5 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_amount",
    4229             :                                    exchange_amount),
    4230             :       NULL),
    4231           5 :     GNUNET_PQ_result_spec_allow_null (
    4232             :       GNUNET_PQ_result_spec_absolute_time ("execution_time",
    4233             :                                            execution_time),
    4234             :       NULL),
    4235           5 :     GNUNET_PQ_result_spec_auto_from_type ("verified",
    4236             :                                           &verified8),
    4237             :     GNUNET_PQ_result_spec_end
    4238             :   };
    4239             :   enum GNUNET_DB_QueryStatus qs;
    4240             : 
    4241           5 :   check_connection (pg);
    4242           5 :   *execution_time = GNUNET_TIME_UNIT_ZERO_ABS;
    4243           5 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    4244             :                                                  "lookup_transfer",
    4245             :                                                  params,
    4246             :                                                  rs);
    4247           5 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    4248             :               "Lookup transfer returned %d\n",
    4249             :               qs);
    4250           5 :   if (qs > 0)
    4251             :   {
    4252           3 :     *have_exchange_sig = ! no_sig;
    4253           3 :     *verified = (0 != verified8);
    4254           6 :     if ( (! no_sig) &&
    4255             :          (0 >
    4256           3 :           TALER_amount_add (total_amount,
    4257             :                             &credit_amount,
    4258             :                             wire_fee)) )
    4259             :     {
    4260           0 :       GNUNET_break (0);
    4261           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    4262             :     }
    4263             :   }
    4264             :   else
    4265             :   {
    4266           2 :     *verified = false;
    4267           2 :     *have_exchange_sig = false;
    4268             :   }
    4269           5 :   return qs;
    4270             : }
    4271             : 
    4272             : 
    4273             : /**
    4274             :  * Set transfer status to verified.
    4275             :  *
    4276             :  * @param cls closure
    4277             :  * @param exchange_url the exchange that made the transfer
    4278             :  * @param wtid wire transfer subject
    4279             :  * @return transaction status
    4280             :  */
    4281             : static enum GNUNET_DB_QueryStatus
    4282           2 : postgres_set_transfer_status_to_verified (
    4283             :   void *cls,
    4284             :   const char *exchange_url,
    4285             :   const struct TALER_WireTransferIdentifierRawP *wtid)
    4286             : {
    4287           2 :   struct PostgresClosure *pg = cls;
    4288           2 :   struct GNUNET_PQ_QueryParam params[] = {
    4289           2 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    4290           2 :     GNUNET_PQ_query_param_string (exchange_url),
    4291             :     GNUNET_PQ_query_param_end
    4292             :   };
    4293             : 
    4294           2 :   check_connection (pg);
    4295           2 :   return GNUNET_PQ_eval_prepared_non_select (
    4296             :     pg->conn,
    4297             :     "set_transfer_status_to_verified",
    4298             :     params);
    4299             : }
    4300             : 
    4301             : 
    4302             : /**
    4303             :  * Closure for #lookup_transfer_summary_cb().
    4304             :  */
    4305             : struct LookupTransferSummaryContext
    4306             : {
    4307             :   /**
    4308             :    * Function to call for each order that was aggregated.
    4309             :    */
    4310             :   TALER_MERCHANTDB_TransferSummaryCallback cb;
    4311             : 
    4312             :   /**
    4313             :    * Closure for @e cb.
    4314             :    */
    4315             :   void *cb_cls;
    4316             : 
    4317             :   /**
    4318             :    * Plugin context.
    4319             :    */
    4320             :   struct PostgresClosure *pg;
    4321             : 
    4322             :   /**
    4323             :    * Transaction result.
    4324             :    */
    4325             :   enum GNUNET_DB_QueryStatus qs;
    4326             : };
    4327             : 
    4328             : 
    4329             : /**
    4330             :  * Function to be called with the results of a SELECT statement
    4331             :  * that has returned @a num_results results.
    4332             :  *
    4333             :  * @param cls of type `struct LookupTransferSummaryContext *`
    4334             :  * @param result the postgres result
    4335             :  * @param num_results the number of results in @a result
    4336             :  */
    4337             : static void
    4338           2 : lookup_transfer_summary_cb (void *cls,
    4339             :                             PGresult *result,
    4340             :                             unsigned int num_results)
    4341             : {
    4342           2 :   struct LookupTransferSummaryContext *ltdc = cls;
    4343           2 :   struct PostgresClosure *pg = ltdc->pg;
    4344             : 
    4345           4 :   for (unsigned int i = 0; i<num_results; i++)
    4346             :   {
    4347             :     char *order_id;
    4348             :     struct TALER_Amount deposit_value;
    4349             :     struct TALER_Amount deposit_fee;
    4350           2 :     struct GNUNET_PQ_ResultSpec rs[] = {
    4351           2 :       GNUNET_PQ_result_spec_string ("order_id",
    4352             :                                     &order_id),
    4353           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
    4354             :                                    &deposit_value),
    4355           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
    4356             :                                    &deposit_fee),
    4357             :       GNUNET_PQ_result_spec_end
    4358             :     };
    4359             : 
    4360           2 :     if (GNUNET_OK !=
    4361           2 :         GNUNET_PQ_extract_result (result,
    4362             :                                   rs,
    4363             :                                   i))
    4364             :     {
    4365           0 :       GNUNET_break (0);
    4366           0 :       ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    4367           0 :       return;
    4368             :     }
    4369           2 :     ltdc->cb (ltdc->cb_cls,
    4370             :               order_id,
    4371             :               &deposit_value,
    4372             :               &deposit_fee);
    4373           2 :     GNUNET_PQ_cleanup_result (rs);
    4374             :   }
    4375           2 :   ltdc->qs = num_results;
    4376             : }
    4377             : 
    4378             : 
    4379             : /**
    4380             :  * Lookup transfer summary.
    4381             :  *
    4382             :  * @param cls closure
    4383             :  * @param exchange_url the exchange that made the transfer
    4384             :  * @param wtid wire transfer subject
    4385             :  * @param cb function to call with detailed transfer data
    4386             :  * @param cb_cls closure for @a cb
    4387             :  * @return transaction status
    4388             :  */
    4389             : static enum GNUNET_DB_QueryStatus
    4390           2 : postgres_lookup_transfer_summary (
    4391             :   void *cls,
    4392             :   const char *exchange_url,
    4393             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    4394             :   TALER_MERCHANTDB_TransferSummaryCallback cb,
    4395             :   void *cb_cls)
    4396             : {
    4397           2 :   struct PostgresClosure *pg = cls;
    4398           2 :   struct GNUNET_PQ_QueryParam params[] = {
    4399           2 :     GNUNET_PQ_query_param_string (exchange_url),
    4400           2 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    4401             :     GNUNET_PQ_query_param_end
    4402             :   };
    4403           2 :   struct LookupTransferSummaryContext ltdc = {
    4404             :     .cb  = cb,
    4405             :     .cb_cls = cb_cls,
    4406             :     .pg = pg
    4407             :   };
    4408             :   enum GNUNET_DB_QueryStatus qs;
    4409             : 
    4410           2 :   check_connection (pg);
    4411           2 :   qs = GNUNET_PQ_eval_prepared_multi_select (
    4412             :     pg->conn,
    4413             :     "lookup_transfer_summary",
    4414             :     params,
    4415             :     &lookup_transfer_summary_cb,
    4416             :     &ltdc);
    4417           2 :   if (0 >= qs)
    4418           0 :     return qs;
    4419           2 :   return ltdc.qs;
    4420             : }
    4421             : 
    4422             : 
    4423             : /**
    4424             :  * Closure for #lookup_transfer_details_cb().
    4425             :  */
    4426             : struct LookupTransferDetailsContext
    4427             : {
    4428             :   /**
    4429             :    * Function to call for each order that was aggregated.
    4430             :    */
    4431             :   TALER_MERCHANTDB_TransferDetailsCallback cb;
    4432             : 
    4433             :   /**
    4434             :    * Closure for @e cb.
    4435             :    */
    4436             :   void *cb_cls;
    4437             : 
    4438             :   /**
    4439             :    * Plugin context.
    4440             :    */
    4441             :   struct PostgresClosure *pg;
    4442             : 
    4443             :   /**
    4444             :    * Transaction result.
    4445             :    */
    4446             :   enum GNUNET_DB_QueryStatus qs;
    4447             : };
    4448             : 
    4449             : 
    4450             : /**
    4451             :  * Function to be called with the results of a SELECT statement
    4452             :  * that has returned @a num_results results.
    4453             :  *
    4454             :  * @param cls of type `struct LookupTransferDetailsContext *`
    4455             :  * @param result the postgres result
    4456             :  * @param num_results the number of results in @a result
    4457             :  */
    4458             : static void
    4459           2 : lookup_transfer_details_cb (void *cls,
    4460             :                             PGresult *result,
    4461             :                             unsigned int num_results)
    4462             : {
    4463           2 :   struct LookupTransferDetailsContext *ltdc = cls;
    4464           2 :   struct PostgresClosure *pg = ltdc->pg;
    4465             : 
    4466           4 :   for (unsigned int i = 0; i<num_results; i++)
    4467             :   {
    4468             :     uint64_t current_offset;
    4469             :     struct TALER_TrackTransferDetails ttd;
    4470           2 :     struct GNUNET_PQ_ResultSpec rs[] = {
    4471           2 :       GNUNET_PQ_result_spec_uint64 ("offset_in_exchange_list",
    4472             :                                     &current_offset),
    4473           2 :       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
    4474             :                                             &ttd.h_contract_terms),
    4475           2 :       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
    4476             :                                             &ttd.coin_pub),
    4477           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
    4478             :                                    &ttd.coin_value),
    4479           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
    4480             :                                    &ttd.coin_fee),
    4481             :       GNUNET_PQ_result_spec_end
    4482             :     };
    4483             : 
    4484           2 :     if (GNUNET_OK !=
    4485           2 :         GNUNET_PQ_extract_result (result,
    4486             :                                   rs,
    4487             :                                   i))
    4488             :     {
    4489           0 :       GNUNET_break (0);
    4490           0 :       ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    4491           0 :       return;
    4492             :     }
    4493           2 :     ltdc->cb (ltdc->cb_cls,
    4494             :               (unsigned int) current_offset,
    4495             :               &ttd);
    4496           2 :     GNUNET_PQ_cleanup_result (rs);
    4497             :   }
    4498           2 :   ltdc->qs = num_results;
    4499             : }
    4500             : 
    4501             : 
    4502             : /**
    4503             :  * Lookup transfer details.
    4504             :  *
    4505             :  * @param cls closure
    4506             :  * @param exchange_url the exchange that made the transfer
    4507             :  * @param wtid wire transfer subject
    4508             :  * @param cb function to call with detailed transfer data
    4509             :  * @param cb_cls closure for @a cb
    4510             :  * @return transaction status
    4511             :  */
    4512             : static enum GNUNET_DB_QueryStatus
    4513           2 : postgres_lookup_transfer_details (
    4514             :   void *cls,
    4515             :   const char *exchange_url,
    4516             :   const struct TALER_WireTransferIdentifierRawP *wtid,
    4517             :   TALER_MERCHANTDB_TransferDetailsCallback cb,
    4518             :   void *cb_cls)
    4519             : {
    4520           2 :   struct PostgresClosure *pg = cls;
    4521           2 :   struct GNUNET_PQ_QueryParam params[] = {
    4522           2 :     GNUNET_PQ_query_param_string (exchange_url),
    4523           2 :     GNUNET_PQ_query_param_auto_from_type (wtid),
    4524             :     GNUNET_PQ_query_param_end
    4525             :   };
    4526           2 :   struct LookupTransferDetailsContext ltdc = {
    4527             :     .cb  = cb,
    4528             :     .cb_cls = cb_cls,
    4529             :     .pg = pg
    4530             :   };
    4531             :   enum GNUNET_DB_QueryStatus qs;
    4532             : 
    4533           2 :   check_connection (pg);
    4534           2 :   qs = GNUNET_PQ_eval_prepared_multi_select (
    4535             :     pg->conn,
    4536             :     "lookup_transfer_details",
    4537             :     params,
    4538             :     &lookup_transfer_details_cb,
    4539             :     &ltdc);
    4540           2 :   if (0 >= qs)
    4541           0 :     return qs;
    4542           2 :   return ltdc.qs;
    4543             : }
    4544             : 
    4545             : 
    4546             : /**
    4547             :  * Closure for #lookup_transfers_cb().
    4548             :  */
    4549             : struct LookupTransfersContext
    4550             : {
    4551             :   /**
    4552             :    * Function to call on results.
    4553             :    */
    4554             :   TALER_MERCHANTDB_TransferCallback cb;
    4555             : 
    4556             :   /**
    4557             :    * Closure for @e cb.
    4558             :    */
    4559             :   void *cb_cls;
    4560             : 
    4561             :   /**
    4562             :    * Postgres context.
    4563             :    */
    4564             :   struct PostgresClosure *pg;
    4565             : 
    4566             :   /**
    4567             :    * Transaction status (set).
    4568             :    */
    4569             :   enum GNUNET_DB_QueryStatus qs;
    4570             : 
    4571             :   /**
    4572             :    * Filter to apply by verification status.
    4573             :    */
    4574             :   enum TALER_EXCHANGE_YesNoAll verified;
    4575             : };
    4576             : 
    4577             : 
    4578             : /**
    4579             :  * Function to be called with the results of a SELECT statement
    4580             :  * that has returned @a num_results results.
    4581             :  *
    4582             :  * @param cls of type `struct LookupTransfersContext *`
    4583             :  * @param result the postgres result
    4584             :  * @param num_results the number of results in @a result
    4585             :  */
    4586             : static void
    4587           2 : lookup_transfers_cb (void *cls,
    4588             :                      PGresult *result,
    4589             :                      unsigned int num_results)
    4590             : {
    4591           2 :   struct LookupTransfersContext *ltc = cls;
    4592           2 :   struct PostgresClosure *pg = ltc->pg;
    4593             : 
    4594           5 :   for (unsigned int i = 0; i<num_results; i++)
    4595             :   {
    4596             :     struct TALER_Amount credit_amount;
    4597             :     struct TALER_WireTransferIdentifierRawP wtid;
    4598             :     char *payto_uri;
    4599             :     char *exchange_url;
    4600             :     uint64_t transfer_serial_id;
    4601             :     struct GNUNET_TIME_Absolute execution_time;
    4602             :     enum TALER_EXCHANGE_YesNoAll verified;
    4603             :     uint8_t verified8;
    4604             :     uint8_t confirmed8;
    4605           3 :     struct GNUNET_PQ_ResultSpec rs[] = {
    4606           3 :       TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount",
    4607             :                                    &credit_amount),
    4608           3 :       GNUNET_PQ_result_spec_auto_from_type ("wtid",
    4609             :                                             &wtid),
    4610           3 :       GNUNET_PQ_result_spec_string ("payto_uri",
    4611             :                                     &payto_uri),
    4612           3 :       GNUNET_PQ_result_spec_string ("exchange_url",
    4613             :                                     &exchange_url),
    4614           3 :       GNUNET_PQ_result_spec_uint64 ("credit_serial",
    4615             :                                     &transfer_serial_id),
    4616           3 :       GNUNET_PQ_result_spec_absolute_time ("execution_time",
    4617             :                                            &execution_time),
    4618           3 :       GNUNET_PQ_result_spec_auto_from_type ("verified",
    4619             :                                             &verified8),
    4620           3 :       GNUNET_PQ_result_spec_auto_from_type ("confirmed",
    4621             :                                             &confirmed8),
    4622             :       GNUNET_PQ_result_spec_end
    4623             :     };
    4624             : 
    4625           3 :     if (GNUNET_OK !=
    4626           3 :         GNUNET_PQ_extract_result (result,
    4627             :                                   rs,
    4628             :                                   i))
    4629             :     {
    4630           0 :       GNUNET_break (0);
    4631           0 :       ltc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    4632           0 :       return;
    4633             :     }
    4634           3 :     if (0 == verified8)
    4635           1 :       verified = TALER_EXCHANGE_YNA_NO;
    4636             :     else
    4637           2 :       verified = TALER_EXCHANGE_YNA_YES;
    4638           3 :     if ( (ltc->verified == TALER_EXCHANGE_YNA_ALL) ||
    4639           0 :          (ltc->verified == verified) )
    4640             :     {
    4641           3 :       ltc->cb (ltc->cb_cls,
    4642             :                &credit_amount,
    4643             :                &wtid,
    4644             :                payto_uri,
    4645             :                exchange_url,
    4646             :                transfer_serial_id,
    4647             :                execution_time,
    4648             :                TALER_EXCHANGE_YNA_YES == verified,
    4649             :                0 != confirmed8);
    4650             :     }
    4651           3 :     GNUNET_PQ_cleanup_result (rs);
    4652             :   }
    4653           2 :   ltc->qs = num_results;
    4654             : }
    4655             : 
    4656             : 
    4657             : /**
    4658             :  * Lookup transfers. Note that filtering by @a verified status is done
    4659             :  * outside of SQL, as we already have 8 prepared statements and adding
    4660             :  * a filter on verified would further double the number of statements for
    4661             :  * a likely rather ineffective filter. So we apply that filter in
    4662             :  * #lookup_transfers_cb().
    4663             :  *
    4664             :  * @param cls closure
    4665             :  * @param instance_id instance to lookup payments for
    4666             :  * @param payto_uri account that we are interested in transfers to
    4667             :  * @param before timestamp for the earliest transfer we care about
    4668             :  * @param after timestamp for the last transfer we care about
    4669             :  * @param limit number of entries to return, negative for descending in execution time,
    4670             :  *                positive for ascending in execution time
    4671             :  * @param offset transfer_serial number of the transfer we want to offset from
    4672             :  * @param verified filter transfers by verification status
    4673             :  * @param cb function to call with detailed transfer data
    4674             :  * @param cb_cls closure for @a cb
    4675             :  * @return transaction status
    4676             :  */
    4677             : static enum GNUNET_DB_QueryStatus
    4678           2 : postgres_lookup_transfers (void *cls,
    4679             :                            const char *instance_id,
    4680             :                            const char *payto_uri,
    4681             :                            struct GNUNET_TIME_Absolute before,
    4682             :                            struct GNUNET_TIME_Absolute after,
    4683             :                            int64_t limit,
    4684             :                            uint64_t offset,
    4685             :                            enum TALER_EXCHANGE_YesNoAll verified,
    4686             :                            TALER_MERCHANTDB_TransferCallback cb,
    4687             :                            void *cb_cls)
    4688             : {
    4689           2 :   struct PostgresClosure *pg = cls;
    4690           2 :   uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit);
    4691           2 :   struct LookupTransfersContext ltc = {
    4692             :     .cb = cb,
    4693             :     .cb_cls = cb_cls,
    4694             :     .pg = pg,
    4695             :     .verified = verified
    4696             :   };
    4697             :   enum GNUNET_DB_QueryStatus qs;
    4698             :   bool by_time;
    4699             : 
    4700           4 :   by_time = ( (before.abs_value_us !=
    4701           4 :                GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) ||
    4702           2 :               (after.abs_value_us !=
    4703           2 :                GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us) );
    4704           2 :   check_connection (pg);
    4705           2 :   if (by_time)
    4706             :   {
    4707           0 :     if (NULL != payto_uri)
    4708             :     {
    4709           0 :       struct GNUNET_PQ_QueryParam params[] = {
    4710           0 :         GNUNET_PQ_query_param_string (instance_id),
    4711           0 :         GNUNET_PQ_query_param_absolute_time (&before),
    4712           0 :         GNUNET_PQ_query_param_absolute_time (&after),
    4713           0 :         GNUNET_PQ_query_param_uint64 (&offset),
    4714           0 :         GNUNET_PQ_query_param_uint64 (&plimit),
    4715           0 :         GNUNET_PQ_query_param_string (payto_uri),
    4716             :         GNUNET_PQ_query_param_end
    4717             :       };
    4718             : 
    4719           0 :       qs = GNUNET_PQ_eval_prepared_multi_select (
    4720             :         pg->conn,
    4721             :         (limit > 0)
    4722             :         ? "lookup_transfers_time_payto_asc"
    4723             :         : "lookup_transfers_time_payto_desc",
    4724             :         params,
    4725             :         &lookup_transfers_cb,
    4726             :         &ltc);
    4727             :     }
    4728             :     else
    4729             :     {
    4730           0 :       struct GNUNET_PQ_QueryParam params[] = {
    4731           0 :         GNUNET_PQ_query_param_string (instance_id),
    4732           0 :         GNUNET_PQ_query_param_absolute_time (&before),
    4733           0 :         GNUNET_PQ_query_param_absolute_time (&after),
    4734           0 :         GNUNET_PQ_query_param_uint64 (&offset),
    4735           0 :         GNUNET_PQ_query_param_uint64 (&plimit),
    4736             :         GNUNET_PQ_query_param_end
    4737             :       };
    4738             : 
    4739           0 :       qs = GNUNET_PQ_eval_prepared_multi_select (
    4740             :         pg->conn,
    4741             :         (limit > 0)
    4742             :         ? "lookup_transfers_time_asc"
    4743             :         : "lookup_transfers_time_desc",
    4744             :         params,
    4745             :         &lookup_transfers_cb,
    4746             :         &ltc);
    4747             :     }
    4748             :   }
    4749             :   else
    4750             :   {
    4751           2 :     if (NULL != payto_uri)
    4752             :     {
    4753           2 :       struct GNUNET_PQ_QueryParam params[] = {
    4754           2 :         GNUNET_PQ_query_param_string (instance_id),
    4755           2 :         GNUNET_PQ_query_param_uint64 (&offset),
    4756           2 :         GNUNET_PQ_query_param_uint64 (&plimit),
    4757           2 :         GNUNET_PQ_query_param_string (payto_uri),
    4758             :         GNUNET_PQ_query_param_end
    4759             :       };
    4760             : 
    4761           2 :       qs = GNUNET_PQ_eval_prepared_multi_select (
    4762             :         pg->conn,
    4763             :         (limit > 0)
    4764             :         ? "lookup_transfers_payto_asc"
    4765             :         : "lookup_transfers_payto_desc",
    4766             :         params,
    4767             :         &lookup_transfers_cb,
    4768             :         &ltc);
    4769             :     }
    4770             :     else
    4771             :     {
    4772           0 :       struct GNUNET_PQ_QueryParam params[] = {
    4773           0 :         GNUNET_PQ_query_param_string (instance_id),
    4774           0 :         GNUNET_PQ_query_param_uint64 (&offset),
    4775           0 :         GNUNET_PQ_query_param_uint64 (&plimit),
    4776             :         GNUNET_PQ_query_param_end
    4777             :       };
    4778             : 
    4779           0 :       qs = GNUNET_PQ_eval_prepared_multi_select (
    4780             :         pg->conn,
    4781             :         (limit > 0)
    4782             :         ? "lookup_transfers_asc"
    4783             :         : "lookup_transfers_desc",
    4784             :         params,
    4785             :         &lookup_transfers_cb,
    4786             :         &ltc);
    4787             :     }
    4788             :   }
    4789           2 :   if (0 >= qs)
    4790           0 :     return qs;
    4791           2 :   return ltc.qs;
    4792             : }
    4793             : 
    4794             : 
    4795             : /**
    4796             :  * Store information about wire fees charged by an exchange,
    4797             :  * including signature (so we have proof).
    4798             :  *
    4799             :  * @param cls closure
    4800             :  * @param master_pub public key of the exchange
    4801             :  * @param h_wire_method hash of wire method
    4802             :  * @param wire_fee wire fee charged
    4803             :  * @param closing_fee closing fee charged (irrelevant for us,
    4804             :  *              but needed to check signature)
    4805             :  * @param start_date start of fee being used
    4806             :  * @param end_date end of fee being used
    4807             :  * @param master_sig signature of exchange over fee structure
    4808             :  * @return transaction status code
    4809             :  */
    4810             : static enum GNUNET_DB_QueryStatus
    4811          35 : postgres_store_wire_fee_by_exchange (
    4812             :   void *cls,
    4813             :   const struct TALER_MasterPublicKeyP *master_pub,
    4814             :   const struct GNUNET_HashCode *h_wire_method,
    4815             :   const struct TALER_Amount *wire_fee,
    4816             :   const struct TALER_Amount *closing_fee,
    4817             :   struct GNUNET_TIME_Absolute start_date,
    4818             :   struct GNUNET_TIME_Absolute end_date,
    4819             :   const struct TALER_MasterSignatureP *master_sig)
    4820             : {
    4821          35 :   struct PostgresClosure *pg = cls;
    4822          35 :   struct GNUNET_PQ_QueryParam params[] = {
    4823          35 :     GNUNET_PQ_query_param_auto_from_type (master_pub),
    4824          35 :     GNUNET_PQ_query_param_auto_from_type (h_wire_method),
    4825          35 :     TALER_PQ_query_param_amount (wire_fee),
    4826          35 :     TALER_PQ_query_param_amount (closing_fee),
    4827          35 :     GNUNET_PQ_query_param_absolute_time (&start_date),
    4828          35 :     GNUNET_PQ_query_param_absolute_time (&end_date),
    4829          35 :     GNUNET_PQ_query_param_auto_from_type (master_sig),
    4830             :     GNUNET_PQ_query_param_end
    4831             :   };
    4832             : 
    4833             :   /* no preflight check here, run in its own transaction by the caller */
    4834          35 :   check_connection (pg);
    4835          35 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    4836             :               "Storing wire fee for %s starting at %s of %s\n",
    4837             :               TALER_B2S (master_pub),
    4838             :               GNUNET_STRINGS_absolute_time_to_string (start_date),
    4839             :               TALER_amount2s (wire_fee));
    4840          35 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4841             :                                              "insert_wire_fee",
    4842             :                                              params);
    4843             : }
    4844             : 
    4845             : 
    4846             : /**
    4847             :  * Add @a credit to a reserve to be used for tipping.  Note that
    4848             :  * this function does not actually perform any wire transfers to
    4849             :  * credit the reserve, it merely tells the merchant backend that
    4850             :  * a reserve now exists.  This has to happen before tips can be
    4851             :  * authorized.
    4852             :  *
    4853             :  * @param cls closure, typically a connection to the db
    4854             :  * @param instance_id which instance is the reserve tied to
    4855             :  * @param reserve_priv which reserve is topped up or created
    4856             :  * @param reserve_pub which reserve is topped up or created
    4857             :  * @param exchange_url what URL is the exchange reachable at where the reserve is located
    4858             :  * @param payto_uri URI to use to fund the reserve
    4859             :  * @param initial_balance how much money will be added to the reserve
    4860             :  * @param expiration when does the reserve expire?
    4861             :  * @return transaction status, usually
    4862             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    4863             :  */
    4864             : static enum TALER_ErrorCode
    4865           4 : postgres_insert_reserve (void *cls,
    4866             :                          const char *instance_id,
    4867             :                          const struct TALER_ReservePrivateKeyP *reserve_priv,
    4868             :                          const struct TALER_ReservePublicKeyP *reserve_pub,
    4869             :                          const char *exchange_url,
    4870             :                          const char *payto_uri,
    4871             :                          const struct TALER_Amount *initial_balance,
    4872             :                          struct GNUNET_TIME_Absolute expiration)
    4873             : {
    4874           4 :   struct PostgresClosure *pg = cls;
    4875             :   unsigned int retries;
    4876             :   enum GNUNET_DB_QueryStatus qs;
    4877             : 
    4878           4 :   retries = 0;
    4879           4 :   check_connection (pg);
    4880           4 : RETRY:
    4881           4 :   if (MAX_RETRIES < ++retries)
    4882           0 :     return TALER_EC_GENERIC_DB_SOFT_FAILURE;
    4883           4 :   if (GNUNET_OK !=
    4884           4 :       postgres_start (pg,
    4885             :                       "insert reserve"))
    4886             :   {
    4887           0 :     GNUNET_break (0);
    4888           0 :     return TALER_EC_GENERIC_DB_START_FAILED;
    4889             :   }
    4890             : 
    4891             :   /* Setup reserve */
    4892             :   {
    4893             :     struct GNUNET_TIME_Absolute now;
    4894           4 :     struct GNUNET_PQ_QueryParam params[] = {
    4895           4 :       GNUNET_PQ_query_param_string (instance_id),
    4896           4 :       GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    4897           4 :       GNUNET_PQ_query_param_absolute_time (&now),
    4898           4 :       GNUNET_PQ_query_param_absolute_time (&expiration),
    4899           4 :       TALER_PQ_query_param_amount (initial_balance),
    4900             :       GNUNET_PQ_query_param_end
    4901             :     };
    4902             : 
    4903           4 :     now = GNUNET_TIME_absolute_get ();
    4904           4 :     (void) GNUNET_TIME_round_abs (&now);
    4905           4 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4906             :                                              "insert_reserve",
    4907             :                                              params);
    4908           4 :     if (0 > qs)
    4909             :     {
    4910           0 :       postgres_rollback (pg);
    4911           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    4912           0 :         goto RETRY;
    4913           0 :       return qs;
    4914             :     }
    4915             :   }
    4916             :   /* Store private key */
    4917             :   {
    4918           4 :     struct GNUNET_PQ_QueryParam params[] = {
    4919           4 :       GNUNET_PQ_query_param_string (instance_id),
    4920           4 :       GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    4921           4 :       GNUNET_PQ_query_param_auto_from_type (reserve_priv),
    4922           4 :       GNUNET_PQ_query_param_string (exchange_url),
    4923           4 :       GNUNET_PQ_query_param_string (payto_uri),
    4924             :       GNUNET_PQ_query_param_end
    4925             :     };
    4926             : 
    4927           4 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4928             :                                              "insert_reserve_key",
    4929             :                                              params);
    4930           4 :     if (0 > qs)
    4931             :     {
    4932           0 :       postgres_rollback (pg);
    4933           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    4934           0 :         goto RETRY;
    4935           0 :       return qs;
    4936             :     }
    4937             :   }
    4938           4 :   qs = postgres_commit (pg);
    4939           4 :   if (0 <= qs)
    4940           4 :     return TALER_EC_NONE; /* success  */
    4941           0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    4942           0 :     goto RETRY;
    4943           0 :   return qs;
    4944             : }
    4945             : 
    4946             : 
    4947             : /**
    4948             :  * Confirms @a credit as the amount the exchange claims to have received and
    4949             :  * thus really 'activates' the reserve.  This has to happen before tips can
    4950             :  * be authorized.
    4951             :  *
    4952             :  * @param cls closure, typically a connection to the db
    4953             :  * @param instance_id which instance is the reserve tied to
    4954             :  * @param reserve_pub which reserve is topped up or created
    4955             :  * @param initial_exchange_balance how much money was be added to the reserve
    4956             :  *           according to the exchange
    4957             :  * @return transaction status, usually
    4958             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    4959             :  */
    4960             : static enum GNUNET_DB_QueryStatus
    4961           4 : postgres_activate_reserve (void *cls,
    4962             :                            const char *instance_id,
    4963             :                            const struct TALER_ReservePublicKeyP *reserve_pub,
    4964             :                            const struct TALER_Amount *initial_exchange_balance)
    4965             : {
    4966           4 :   struct PostgresClosure *pg = cls;
    4967           4 :   struct GNUNET_PQ_QueryParam params[] = {
    4968           4 :     GNUNET_PQ_query_param_string (instance_id),
    4969           4 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    4970           4 :     TALER_PQ_query_param_amount (initial_exchange_balance),
    4971             :     GNUNET_PQ_query_param_end
    4972             :   };
    4973             : 
    4974           4 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    4975             :                                              "activate_reserve",
    4976             :                                              params);
    4977             : }
    4978             : 
    4979             : 
    4980             : /**
    4981             :  * Closure for #lookup_reserves_cb.
    4982             :  */
    4983             : struct LookupReservesContext
    4984             : {
    4985             :   /**
    4986             :    * Postgres context.
    4987             :    */
    4988             :   struct PostgresClosure *pg;
    4989             : 
    4990             :   /**
    4991             :    * Function to call with the results
    4992             :    */
    4993             :   TALER_MERCHANTDB_ReservesCallback cb;
    4994             : 
    4995             :   /**
    4996             :    * Closure for @e cb
    4997             :    */
    4998             :   void *cb_cls;
    4999             : 
    5000             :   /**
    5001             :    * Filter by active reserves.
    5002             :    */
    5003             :   enum TALER_EXCHANGE_YesNoAll active;
    5004             : 
    5005             :   /**
    5006             :    * Filter by failures (mismatch in exchange claimed and
    5007             :    * merchant claimed initial amounts).
    5008             :    */
    5009             :   enum TALER_EXCHANGE_YesNoAll failures;
    5010             : 
    5011             :   /**
    5012             :    * Set in case of errors.
    5013             :    */
    5014             :   enum GNUNET_DB_QueryStatus qs;
    5015             : 
    5016             : };
    5017             : 
    5018             : 
    5019             : /**
    5020             :  * Function to be called with the results of a SELECT statement
    5021             :  * that has returned @a num_results results about accounts.
    5022             :  *
    5023             :  * @param[in,out] cls of type `struct LookupReservesContext *`
    5024             :  * @param result the postgres result
    5025             :  * @param num_results the number of results in @a result
    5026             :  */
    5027             : static void
    5028           3 : lookup_reserves_cb (void *cls,
    5029             :                     PGresult *result,
    5030             :                     unsigned int num_results)
    5031             : {
    5032           3 :   struct LookupReservesContext *lrc = cls;
    5033           3 :   struct PostgresClosure *pg = lrc->pg;
    5034             : 
    5035           7 :   for (unsigned int i = 0; i < num_results; i++)
    5036             :   {
    5037             :     struct TALER_ReservePublicKeyP reserve_pub;
    5038             :     struct GNUNET_TIME_Absolute creation_time;
    5039             :     struct GNUNET_TIME_Absolute expiration_time;
    5040             :     struct TALER_Amount merchant_initial_balance;
    5041             :     struct TALER_Amount exchange_initial_balance;
    5042             :     struct TALER_Amount pickup_amount;
    5043             :     struct TALER_Amount committed_amount;
    5044             :     uint8_t active;
    5045           4 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5046           4 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    5047             :                                             &reserve_pub),
    5048           4 :       GNUNET_PQ_result_spec_absolute_time ("creation_time",
    5049             :                                            &creation_time),
    5050           4 :       GNUNET_PQ_result_spec_absolute_time ("expiration",
    5051             :                                            &expiration_time),
    5052           4 :       TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
    5053             :                                    &merchant_initial_balance),
    5054           4 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
    5055             :                                    &exchange_initial_balance),
    5056           4 :       TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
    5057             :                                    &committed_amount),
    5058           4 :       TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
    5059             :                                    &pickup_amount),
    5060           4 :       GNUNET_PQ_result_spec_auto_from_type ("active",
    5061             :                                             &active),
    5062             :       GNUNET_PQ_result_spec_end
    5063             :     };
    5064             : 
    5065           4 :     if (GNUNET_OK !=
    5066           4 :         GNUNET_PQ_extract_result (result,
    5067             :                                   rs,
    5068             :                                   i))
    5069             :     {
    5070           0 :       GNUNET_break (0);
    5071           0 :       lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    5072           0 :       return;
    5073             :     }
    5074           4 :     switch (lrc->active)
    5075             :     {
    5076           0 :     case TALER_EXCHANGE_YNA_YES:
    5077           0 :       if (0 == active)
    5078           0 :         continue;
    5079           0 :       break;
    5080           0 :     case TALER_EXCHANGE_YNA_NO:
    5081           0 :       if (0 != active)
    5082           0 :         continue;
    5083           0 :       break;
    5084           4 :     case TALER_EXCHANGE_YNA_ALL:
    5085           4 :       break;
    5086             :     }
    5087           4 :     switch (lrc->failures)
    5088             :     {
    5089           0 :     case TALER_EXCHANGE_YNA_YES:
    5090           0 :       if (0 ==
    5091           0 :           TALER_amount_cmp (&merchant_initial_balance,
    5092             :                             &exchange_initial_balance))
    5093           0 :         continue;
    5094           0 :       break;
    5095           0 :     case TALER_EXCHANGE_YNA_NO:
    5096           0 :       if (0 !=
    5097           0 :           TALER_amount_cmp (&merchant_initial_balance,
    5098             :                             &exchange_initial_balance))
    5099           0 :         continue;
    5100           0 :       break;
    5101           4 :     case TALER_EXCHANGE_YNA_ALL:
    5102           4 :       break;
    5103             :     }
    5104           4 :     lrc->cb (lrc->cb_cls,
    5105             :              &reserve_pub,
    5106             :              creation_time,
    5107             :              expiration_time,
    5108             :              &merchant_initial_balance,
    5109             :              &exchange_initial_balance,
    5110             :              &pickup_amount,
    5111             :              &committed_amount,
    5112             :              (0 != active));
    5113             :   }
    5114             : }
    5115             : 
    5116             : 
    5117             : /**
    5118             :  * Lookup reserves.
    5119             :  *
    5120             :  * @param cls closure
    5121             :  * @param instance_id instance to lookup payments for
    5122             :  * @param created_after filter by reserves created after this date
    5123             :  * @param active filter by active reserves
    5124             :  * @param failures filter by reserves with a disagreement on the initial balance
    5125             :  * @param cb function to call with reserve summary data
    5126             :  * @param cb_cls closure for @a cb
    5127             :  * @return transaction status
    5128             :  */
    5129             : static enum GNUNET_DB_QueryStatus
    5130           3 : postgres_lookup_reserves (void *cls,
    5131             :                           const char *instance_id,
    5132             :                           struct GNUNET_TIME_Absolute created_after,
    5133             :                           enum TALER_EXCHANGE_YesNoAll active,
    5134             :                           enum TALER_EXCHANGE_YesNoAll failures,
    5135             :                           TALER_MERCHANTDB_ReservesCallback cb,
    5136             :                           void *cb_cls)
    5137             : {
    5138           3 :   struct PostgresClosure *pg = cls;
    5139           3 :   struct LookupReservesContext lrc = {
    5140             :     .pg = pg,
    5141             :     .active = active,
    5142             :     .failures = failures,
    5143             :     .cb = cb,
    5144             :     .cb_cls = cb_cls
    5145             :   };
    5146           3 :   struct GNUNET_PQ_QueryParam params[] = {
    5147           3 :     GNUNET_PQ_query_param_string (instance_id),
    5148           3 :     GNUNET_PQ_query_param_absolute_time (&created_after),
    5149             :     GNUNET_PQ_query_param_end
    5150             :   };
    5151             :   enum GNUNET_DB_QueryStatus qs;
    5152             : 
    5153           3 :   check_connection (pg);
    5154           3 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5155             :                                              "lookup_reserves",
    5156             :                                              params,
    5157             :                                              &lookup_reserves_cb,
    5158             :                                              &lrc);
    5159           3 :   if (lrc.qs < 0)
    5160           0 :     return lrc.qs;
    5161           3 :   return qs;
    5162             : }
    5163             : 
    5164             : 
    5165             : /**
    5166             :  * Closure for #lookup_pending_reserves_cb.
    5167             :  */
    5168             : struct LookupPendingReservesContext
    5169             : {
    5170             :   /**
    5171             :    * Postgres context.
    5172             :    */
    5173             :   struct PostgresClosure *pg;
    5174             : 
    5175             :   /**
    5176             :    * Function to call with the results
    5177             :    */
    5178             :   TALER_MERCHANTDB_PendingReservesCallback cb;
    5179             : 
    5180             :   /**
    5181             :    * Closure for @e cb
    5182             :    */
    5183             :   void *cb_cls;
    5184             : 
    5185             :   /**
    5186             :    * Set in case of errors.
    5187             :    */
    5188             :   enum GNUNET_DB_QueryStatus qs;
    5189             : 
    5190             : };
    5191             : 
    5192             : 
    5193             : /**
    5194             :  * Function to be called with the results of a SELECT statement
    5195             :  * that has returned @a num_results results about accounts.
    5196             :  *
    5197             :  * @param[in,out] cls of type `struct LookupReservesContext *`
    5198             :  * @param result the postgres result
    5199             :  * @param num_results the number of results in @a result
    5200             :  */
    5201             : static void
    5202          18 : lookup_pending_reserves_cb (void *cls,
    5203             :                             PGresult *result,
    5204             :                             unsigned int num_results)
    5205             : {
    5206          18 :   struct LookupPendingReservesContext *lrc = cls;
    5207          18 :   struct PostgresClosure *pg = lrc->pg;
    5208             : 
    5209          19 :   for (unsigned int i = 0; i < num_results; i++)
    5210             :   {
    5211             :     struct TALER_ReservePublicKeyP reserve_pub;
    5212             :     struct TALER_Amount merchant_initial_balance;
    5213             :     char *exchange_url;
    5214             :     char *instance_id;
    5215           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5216           1 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    5217             :                                             &reserve_pub),
    5218           1 :       GNUNET_PQ_result_spec_string ("merchant_id",
    5219             :                                     &instance_id),
    5220           1 :       GNUNET_PQ_result_spec_string ("exchange_url",
    5221             :                                     &exchange_url),
    5222           1 :       TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
    5223             :                                    &merchant_initial_balance),
    5224             :       GNUNET_PQ_result_spec_end
    5225             :     };
    5226             : 
    5227           1 :     if (GNUNET_OK !=
    5228           1 :         GNUNET_PQ_extract_result (result,
    5229             :                                   rs,
    5230             :                                   i))
    5231             :     {
    5232           0 :       GNUNET_break (0);
    5233           0 :       lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    5234           0 :       return;
    5235             :     }
    5236           1 :     lrc->cb (lrc->cb_cls,
    5237             :              instance_id,
    5238             :              exchange_url,
    5239             :              &reserve_pub,
    5240             :              &merchant_initial_balance);
    5241           1 :     GNUNET_PQ_cleanup_result (rs);
    5242             :   }
    5243             : }
    5244             : 
    5245             : 
    5246             : /**
    5247             :  * Lookup reserves pending activation across all instances.
    5248             :  *
    5249             :  * @param cls closure
    5250             :  * @param cb function to call with reserve summary data
    5251             :  * @param cb_cls closure for @a cb
    5252             :  * @return transaction status
    5253             :  */
    5254             : static enum GNUNET_DB_QueryStatus
    5255          18 : postgres_lookup_pending_reserves (void *cls,
    5256             :                                   TALER_MERCHANTDB_PendingReservesCallback cb,
    5257             :                                   void *cb_cls)
    5258             : {
    5259          18 :   struct PostgresClosure *pg = cls;
    5260          18 :   struct LookupPendingReservesContext lrc = {
    5261             :     .pg = pg,
    5262             :     .cb = cb,
    5263             :     .cb_cls = cb_cls
    5264             :   };
    5265          18 :   struct GNUNET_PQ_QueryParam params[] = {
    5266             :     GNUNET_PQ_query_param_end
    5267             :   };
    5268             :   enum GNUNET_DB_QueryStatus qs;
    5269             : 
    5270          18 :   check_connection (pg);
    5271          18 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5272             :                                              "lookup_pending_reserves",
    5273             :                                              params,
    5274             :                                              &lookup_pending_reserves_cb,
    5275             :                                              &lrc);
    5276          18 :   if (lrc.qs < 0)
    5277           0 :     return lrc.qs;
    5278          18 :   return qs;
    5279             : }
    5280             : 
    5281             : 
    5282             : /**
    5283             :  * Closure for #lookup_reserve_tips_cb().
    5284             :  */
    5285             : struct LookupTipsContext
    5286             : {
    5287             :   /**
    5288             :    * Postgres context.
    5289             :    */
    5290             :   struct PostgresClosure *pg;
    5291             : 
    5292             :   /**
    5293             :    * Array with information about tips generated from this reserve.
    5294             :    */
    5295             :   struct TALER_MERCHANTDB_TipDetails *tips;
    5296             : 
    5297             :   /**
    5298             :    * Length of the @e tips array.
    5299             :    */
    5300             :   unsigned int tips_length;
    5301             : 
    5302             :   /**
    5303             :    * Set in case of errors.
    5304             :    */
    5305             :   enum GNUNET_DB_QueryStatus qs;
    5306             : };
    5307             : 
    5308             : 
    5309             : /**
    5310             :  * Function to be called with the results of a SELECT statement
    5311             :  * that has returned @a num_results results about accounts.
    5312             :  *
    5313             :  * @param[in,out] cls of type `struct LookupTipsContext *`
    5314             :  * @param result the postgres result
    5315             :  * @param num_results the number of results in @a result
    5316             :  */
    5317             : static void
    5318           1 : lookup_reserve_tips_cb (void *cls,
    5319             :                         PGresult *result,
    5320             :                         unsigned int num_results)
    5321             : {
    5322           1 :   struct LookupTipsContext *ltc = cls;
    5323           1 :   struct PostgresClosure *pg = ltc->pg;
    5324             : 
    5325           1 :   GNUNET_array_grow (ltc->tips,
    5326             :                      ltc->tips_length,
    5327             :                      num_results);
    5328           3 :   for (unsigned int i = 0; i < num_results; i++)
    5329             :   {
    5330           2 :     struct TALER_MERCHANTDB_TipDetails *td = &ltc->tips[i];
    5331           2 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5332           2 :       GNUNET_PQ_result_spec_string ("justification",
    5333             :                                     &td->reason),
    5334           2 :       GNUNET_PQ_result_spec_auto_from_type ("tip_id",
    5335             :                                             &td->tip_id),
    5336           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    5337             :                                    &td->total_amount),
    5338             :       GNUNET_PQ_result_spec_end
    5339             :     };
    5340             : 
    5341           2 :     if (GNUNET_OK !=
    5342           2 :         GNUNET_PQ_extract_result (result,
    5343             :                                   rs,
    5344             :                                   i))
    5345             :     {
    5346           0 :       GNUNET_break (0);
    5347           0 :       ltc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    5348           0 :       return;
    5349             :     }
    5350             :   }
    5351             : }
    5352             : 
    5353             : 
    5354             : /**
    5355             :  * Lookup reserve details.
    5356             :  *
    5357             :  * @param cls closure
    5358             :  * @param instance_id instance to lookup payments for
    5359             :  * @param reserve_pub public key of the reserve to inspect
    5360             :  * @param fetch_tips if true, also return information about tips
    5361             :  * @param cb function to call with reserve summary data
    5362             :  * @param cb_cls closure for @a cb
    5363             :  * @return transaction status
    5364             :  */
    5365             : static enum GNUNET_DB_QueryStatus
    5366           3 : postgres_lookup_reserve (void *cls,
    5367             :                          const char *instance_id,
    5368             :                          const struct TALER_ReservePublicKeyP *reserve_pub,
    5369             :                          bool fetch_tips,
    5370             :                          TALER_MERCHANTDB_ReserveDetailsCallback cb,
    5371             :                          void *cb_cls)
    5372             : {
    5373           3 :   struct PostgresClosure *pg = cls;
    5374           3 :   struct LookupTipsContext ltc = {
    5375             :     .pg = pg,
    5376             :     .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    5377             :   };
    5378           3 :   struct GNUNET_PQ_QueryParam params[] = {
    5379           3 :     GNUNET_PQ_query_param_string (instance_id),
    5380           3 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    5381             :     GNUNET_PQ_query_param_end
    5382             :   };
    5383             :   struct GNUNET_TIME_Absolute creation_time;
    5384             :   struct GNUNET_TIME_Absolute expiration_time;
    5385             :   struct TALER_Amount merchant_initial_balance;
    5386             :   struct TALER_Amount exchange_initial_balance;
    5387             :   struct TALER_Amount pickup_amount;
    5388             :   struct TALER_Amount committed_amount;
    5389             :   uint8_t active;
    5390           3 :   char *exchange_url = NULL;
    5391           3 :   char *payto_uri = NULL;
    5392           3 :   struct GNUNET_PQ_ResultSpec rs[] = {
    5393           3 :     GNUNET_PQ_result_spec_absolute_time ("creation_time",
    5394             :                                          &creation_time),
    5395           3 :     GNUNET_PQ_result_spec_absolute_time ("expiration",
    5396             :                                          &expiration_time),
    5397           3 :     TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
    5398             :                                  &merchant_initial_balance),
    5399           3 :     TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
    5400             :                                  &exchange_initial_balance),
    5401           3 :     TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
    5402             :                                  &pickup_amount),
    5403           3 :     TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
    5404             :                                  &committed_amount),
    5405           3 :     GNUNET_PQ_result_spec_auto_from_type ("active",
    5406             :                                           &active),
    5407           3 :     GNUNET_PQ_result_spec_allow_null (
    5408             :       GNUNET_PQ_result_spec_string ("exchange_url",
    5409             :                                     &exchange_url),
    5410             :       NULL),
    5411           3 :     GNUNET_PQ_result_spec_allow_null (
    5412             :       GNUNET_PQ_result_spec_string ("payto_uri",
    5413             :                                     &payto_uri),
    5414             :       NULL),
    5415             :     GNUNET_PQ_result_spec_end
    5416             :   };
    5417             :   enum GNUNET_DB_QueryStatus qs;
    5418             : 
    5419           3 :   check_connection (pg);
    5420           3 :   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5421             :                                                  "lookup_reserve",
    5422             :                                                  params,
    5423             :                                                  rs);
    5424           3 :   if (qs < 0)
    5425           0 :     return qs;
    5426           3 :   if (! fetch_tips)
    5427             :   {
    5428           2 :     cb (cb_cls,
    5429             :         creation_time,
    5430             :         expiration_time,
    5431             :         &merchant_initial_balance,
    5432             :         &exchange_initial_balance,
    5433             :         &pickup_amount,
    5434             :         &committed_amount,
    5435             :         (0 != active),
    5436             :         exchange_url,
    5437             :         payto_uri,
    5438             :         0,
    5439             :         NULL);
    5440           2 :     GNUNET_PQ_cleanup_result (rs);
    5441           2 :     return qs;
    5442             :   }
    5443             : 
    5444           1 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5445             :                                              "lookup_reserve_tips",
    5446             :                                              params,
    5447             :                                              &lookup_reserve_tips_cb,
    5448             :                                              &ltc);
    5449           1 :   if (qs < 0)
    5450           0 :     return qs;
    5451           1 :   if (ltc.qs >= 0)
    5452             :   {
    5453           1 :     cb (cb_cls,
    5454             :         creation_time,
    5455             :         expiration_time,
    5456             :         &merchant_initial_balance,
    5457             :         &exchange_initial_balance,
    5458             :         &pickup_amount,
    5459             :         &committed_amount,
    5460             :         0 != active,
    5461             :         exchange_url,
    5462             :         payto_uri,
    5463             :         ltc.tips_length,
    5464           1 :         ltc.tips);
    5465             :   }
    5466           3 :   for (unsigned int i = 0; i<ltc.tips_length; i++)
    5467           2 :     GNUNET_free (ltc.tips[i].reason);
    5468           1 :   GNUNET_array_grow (ltc.tips,
    5469             :                      ltc.tips_length,
    5470             :                      0);
    5471           1 :   GNUNET_PQ_cleanup_result (rs);
    5472           1 :   return ltc.qs;
    5473             : }
    5474             : 
    5475             : 
    5476             : /**
    5477             :  * Delete a reserve's private key.
    5478             :  *
    5479             :  * @param cls closure, typically a connection to the db
    5480             :  * @param instance_id which instance is the reserve tied to
    5481             :  * @param reserve_pub which reserve is to be deleted
    5482             :  * @return transaction status, usually
    5483             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    5484             :  */
    5485             : static enum GNUNET_DB_QueryStatus
    5486           3 : postgres_delete_reserve (void *cls,
    5487             :                          const char *instance_id,
    5488             :                          const struct TALER_ReservePublicKeyP *reserve_pub)
    5489             : {
    5490           3 :   struct PostgresClosure *pg = cls;
    5491           3 :   struct GNUNET_PQ_QueryParam params[] = {
    5492           3 :     GNUNET_PQ_query_param_string (instance_id),
    5493           3 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    5494             :     GNUNET_PQ_query_param_end
    5495             :   };
    5496             : 
    5497           3 :   check_connection (pg);
    5498           3 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5499             :                                              "delete_reserve",
    5500             :                                              params);
    5501             : }
    5502             : 
    5503             : 
    5504             : /**
    5505             :  * Purge all of the information about a reserve, including tips.
    5506             :  *
    5507             :  * @param cls closure, typically a connection to the db
    5508             :  * @param instance_id which instance is the reserve tied to
    5509             :  * @param reserve_pub which reserve is to be purged
    5510             :  * @return transaction status, usually
    5511             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    5512             :  */
    5513             : static enum GNUNET_DB_QueryStatus
    5514           4 : postgres_purge_reserve (void *cls,
    5515             :                         const char *instance_id,
    5516             :                         const struct TALER_ReservePublicKeyP *reserve_pub)
    5517             : {
    5518           4 :   struct PostgresClosure *pg = cls;
    5519           4 :   struct GNUNET_PQ_QueryParam params[] = {
    5520           4 :     GNUNET_PQ_query_param_string (instance_id),
    5521           4 :     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
    5522             :     GNUNET_PQ_query_param_end
    5523             :   };
    5524             : 
    5525           4 :   check_connection (pg);
    5526           4 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5527             :                                              "purge_reserve",
    5528             :                                              params);
    5529             : }
    5530             : 
    5531             : 
    5532             : /**
    5533             :  * Closure for #lookup_reserve_for_tip_cb().
    5534             :  */
    5535             : struct LookupReserveForTipContext
    5536             : {
    5537             :   /**
    5538             :    * Postgres context.
    5539             :    */
    5540             :   struct PostgresClosure *pg;
    5541             : 
    5542             :   /**
    5543             :    * Public key of the reserve we found.
    5544             :    */
    5545             :   struct TALER_ReservePublicKeyP reserve_pub;
    5546             : 
    5547             :   /**
    5548             :    * How much money must be left in the reserve.
    5549             :    */
    5550             :   struct TALER_Amount required_amount;
    5551             : 
    5552             :   /**
    5553             :    * Set to the expiration time of the reserve we found.
    5554             :    * #GNUNET_TIME_UNIT_FOREVER_ABS if we found none.
    5555             :    */
    5556             :   struct GNUNET_TIME_Absolute expiration;
    5557             : 
    5558             :   /**
    5559             :    * Error status.
    5560             :    */
    5561             :   enum TALER_ErrorCode ec;
    5562             : 
    5563             :   /**
    5564             :    * Did we find a good reserve?
    5565             :    */
    5566             :   bool ok;
    5567             : };
    5568             : 
    5569             : 
    5570             : /**
    5571             :  * How long must a reserve be at least still valid before we use
    5572             :  * it for a tip?
    5573             :  */
    5574             : #define MIN_EXPIRATION GNUNET_TIME_UNIT_HOURS
    5575             : 
    5576             : 
    5577             : /**
    5578             :  * Function to be called with the results of a SELECT statement
    5579             :  * that has returned @a num_results results about accounts.
    5580             :  *
    5581             :  * @param[in,out] cls of type `struct LookupReserveForTipContext *`
    5582             :  * @param result the postgres result
    5583             :  * @param num_results the number of results in @a result
    5584             :  */
    5585             : static void
    5586           1 : lookup_reserve_for_tip_cb (void *cls,
    5587             :                            PGresult *result,
    5588             :                            unsigned int num_results)
    5589             : {
    5590           1 :   struct LookupReserveForTipContext *lac = cls;
    5591           1 :   struct PostgresClosure *pg = lac->pg;
    5592             : 
    5593           2 :   for (unsigned int i = 0; i < num_results; i++)
    5594             :   {
    5595             :     struct TALER_ReservePublicKeyP reserve_pub;
    5596             :     struct TALER_Amount committed_amount;
    5597             :     struct TALER_Amount remaining;
    5598             :     struct TALER_Amount initial_balance;
    5599             :     struct GNUNET_TIME_Absolute expiration;
    5600           1 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5601           1 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    5602             :                                             &reserve_pub),
    5603           1 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
    5604             :                                    &initial_balance),
    5605           1 :       TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
    5606             :                                    &committed_amount),
    5607           1 :       GNUNET_PQ_result_spec_absolute_time ("expiration",
    5608             :                                            &expiration),
    5609             :       GNUNET_PQ_result_spec_end
    5610             :     };
    5611             : 
    5612           1 :     if (GNUNET_OK !=
    5613           1 :         GNUNET_PQ_extract_result (result,
    5614             :                                   rs,
    5615             :                                   i))
    5616             :     {
    5617           0 :       GNUNET_break (0);
    5618           0 :       lac->ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
    5619           0 :       return;
    5620             :     }
    5621           1 :     if (0 >
    5622           1 :         TALER_amount_subtract (&remaining,
    5623             :                                &initial_balance,
    5624             :                                &committed_amount))
    5625             :     {
    5626           0 :       GNUNET_break (0);
    5627           0 :       continue;
    5628             :     }
    5629           1 :     if (0 >
    5630           1 :         TALER_amount_cmp (&remaining,
    5631           1 :                           &lac->required_amount))
    5632             :     {
    5633             :       /* insufficient balance */
    5634           0 :       if (lac->ok)
    5635           0 :         continue;  /* got another reserve */
    5636           0 :       lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
    5637           0 :       continue;
    5638             :     }
    5639           2 :     if ( (lac->expiration.abs_value_us !=
    5640           1 :           GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) &&
    5641           0 :          ( (expiration.abs_value_us > lac->expiration.abs_value_us) &&
    5642           0 :            (GNUNET_TIME_absolute_get_remaining (lac->expiration).rel_value_us >
    5643           0 :             MIN_EXPIRATION.rel_value_us) ) )
    5644             :     {
    5645             :       /* reserve expired */
    5646           0 :       if (lac->ok)
    5647           0 :         continue; /* got another reserve */
    5648           0 :       lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED;
    5649           0 :       continue;
    5650             :     }
    5651           1 :     lac->ok = true;
    5652           1 :     lac->ec = TALER_EC_NONE;
    5653           1 :     lac->expiration = expiration;
    5654           1 :     lac->reserve_pub = reserve_pub;
    5655             :   }
    5656             : }
    5657             : 
    5658             : 
    5659             : /**
    5660             :  * Authorize a tip over @a amount from reserve @a reserve_pub.  Remember
    5661             :  * the authorization under @a tip_id for later, together with the
    5662             :  * @a justification.
    5663             :  *
    5664             :  * @param cls closure, typically a connection to the db
    5665             :  * @param instance_id which instance should generate the tip
    5666             :  * @param reserve_pub which reserve is debited, NULL to pick one in the DB
    5667             :  * @param amount how high is the tip (with fees)
    5668             :  * @param justification why was the tip approved
    5669             :  * @param next_url where to send the URL post tip pickup
    5670             :  * @param[out] tip_id set to the unique ID for the tip
    5671             :  * @param[out] expiration set to when the tip expires
    5672             :  * @return transaction status,
    5673             :  *      #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED if the reserve is known but has expired
    5674             :  *      #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND if the reserve is not known
    5675             :  *      #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS if the reserve has insufficient funds left
    5676             :  *      #TALER_EC_GENERIC_DB_START_FAILED on hard DB errors
    5677             :  *      #TALER_EC_GENERIC_DB_FETCH_FAILED on hard DB errors
    5678             :  *      #TALER_EC_GENERIC_DB_STORE_FAILED on hard DB errors
    5679             :  *      #TALER_EC_GENERIC_DB_INVARIANT_FAILURE on hard DB errors
    5680             :  *      #TALER_EC_GENERIC_DB_SOFT_FAILURE soft DB errors (client should retry)
    5681             :  *      #TALER_EC_NONE upon success
    5682             :  */
    5683             : static enum TALER_ErrorCode
    5684          11 : postgres_authorize_tip (void *cls,
    5685             :                         const char *instance_id,
    5686             :                         const struct TALER_ReservePublicKeyP *reserve_pub,
    5687             :                         const struct TALER_Amount *amount,
    5688             :                         const char *justification,
    5689             :                         const char *next_url,
    5690             :                         struct GNUNET_HashCode *tip_id,
    5691             :                         struct GNUNET_TIME_Absolute *expiration)
    5692             : {
    5693          11 :   struct PostgresClosure *pg = cls;
    5694          11 :   unsigned int retries = 0;
    5695             :   enum GNUNET_DB_QueryStatus qs;
    5696             :   struct TALER_Amount tips_committed;
    5697             :   struct TALER_Amount exchange_initial_balance;
    5698             :   const struct TALER_ReservePublicKeyP *reserve_pubp;
    5699          11 :   struct LookupReserveForTipContext lac = {
    5700             :     .pg = pg,
    5701             :     .required_amount = *amount,
    5702          11 :     .expiration = GNUNET_TIME_UNIT_FOREVER_ABS
    5703             :   };
    5704             : 
    5705          11 :   check_connection (pg);
    5706          11 : RETRY:
    5707          11 :   reserve_pubp = reserve_pub;
    5708          11 :   if (MAX_RETRIES < ++retries)
    5709             :   {
    5710           0 :     GNUNET_break (0);
    5711             :     return
    5712           0 :       TALER_EC_GENERIC_DB_SOFT_FAILURE;
    5713             :   }
    5714          11 :   if (GNUNET_OK !=
    5715          11 :       postgres_start (pg,
    5716             :                       "enable tip reserve"))
    5717             :   {
    5718           0 :     GNUNET_break (0);
    5719           0 :     return TALER_EC_GENERIC_DB_START_FAILED;
    5720             :   }
    5721          11 :   if (NULL == reserve_pubp)
    5722             :   {
    5723           1 :     struct GNUNET_PQ_QueryParam params[] = {
    5724           1 :       GNUNET_PQ_query_param_string (instance_id),
    5725             :       GNUNET_PQ_query_param_end
    5726             :     };
    5727             : 
    5728           1 :     qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    5729             :                                                "lookup_reserve_for_tip",
    5730             :                                                params,
    5731             :                                                &lookup_reserve_for_tip_cb,
    5732             :                                                &lac);
    5733           1 :     switch (qs)
    5734             :     {
    5735           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    5736           0 :       postgres_rollback (pg);
    5737           0 :       goto RETRY;
    5738           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    5739           0 :       GNUNET_break (0);
    5740           0 :       postgres_rollback (pg);
    5741           0 :       return TALER_EC_GENERIC_DB_FETCH_FAILED;
    5742           0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    5743           0 :       postgres_rollback (pg);
    5744           0 :       return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND;
    5745           1 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    5746             :     default:
    5747           1 :       break;
    5748             :     }
    5749           1 :     if (TALER_EC_NONE  != lac.ec)
    5750           0 :       return lac.ec;
    5751           1 :     GNUNET_assert (lac.ok);
    5752           1 :     reserve_pubp = &lac.reserve_pub;
    5753             :   }
    5754             : 
    5755             :   {
    5756          11 :     struct GNUNET_PQ_QueryParam params[] = {
    5757          11 :       GNUNET_PQ_query_param_string (instance_id),
    5758          11 :       GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
    5759             :       GNUNET_PQ_query_param_end
    5760             :     };
    5761          11 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5762          11 :       GNUNET_PQ_result_spec_absolute_time ("expiration",
    5763             :                                            expiration),
    5764          11 :       TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
    5765             :                                    &tips_committed),
    5766          11 :       TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
    5767             :                                    &exchange_initial_balance),
    5768             :       GNUNET_PQ_result_spec_end
    5769             :     };
    5770             : 
    5771          11 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5772             :                                                    "lookup_reserve_status",
    5773             :                                                    params,
    5774             :                                                    rs);
    5775          11 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    5776             :     {
    5777           0 :       postgres_rollback (pg);
    5778           0 :       goto RETRY;
    5779             :     }
    5780          11 :     if (qs < 0)
    5781             :     {
    5782           0 :       GNUNET_break (0);
    5783           0 :       postgres_rollback (pg);
    5784             :       return
    5785           1 :         TALER_EC_GENERIC_DB_FETCH_FAILED;
    5786             :     }
    5787          11 :     if (0 == qs)
    5788             :     {
    5789           1 :       postgres_rollback (pg);
    5790           1 :       return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND;
    5791             :     }
    5792             :   }
    5793             :   {
    5794             :     struct TALER_Amount remaining;
    5795             : 
    5796          10 :     if (0 >
    5797          10 :         TALER_amount_subtract (&remaining,
    5798             :                                &exchange_initial_balance,
    5799             :                                &tips_committed))
    5800             :     {
    5801           0 :       GNUNET_break (0);
    5802           0 :       postgres_rollback (pg);
    5803           2 :       return TALER_EC_GENERIC_DB_INVARIANT_FAILURE;
    5804             :     }
    5805          10 :     if (0 >
    5806          10 :         TALER_amount_cmp (&remaining,
    5807             :                           amount))
    5808             :     {
    5809           2 :       postgres_rollback (pg);
    5810           2 :       return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
    5811             :     }
    5812             :   }
    5813           8 :   GNUNET_assert (0 <=
    5814             :                  TALER_amount_add (&tips_committed,
    5815             :                                    &tips_committed,
    5816             :                                    amount));
    5817             :   {
    5818           8 :     struct GNUNET_PQ_QueryParam params[] = {
    5819           8 :       GNUNET_PQ_query_param_string (instance_id),
    5820           8 :       GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
    5821           8 :       TALER_PQ_query_param_amount (&tips_committed),
    5822             :       GNUNET_PQ_query_param_end
    5823             :     };
    5824             : 
    5825           8 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5826             :                                              "update_reserve_tips_committed",
    5827             :                                              params);
    5828           8 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    5829             :     {
    5830           0 :       postgres_rollback (pg);
    5831           0 :       goto RETRY;
    5832             :     }
    5833           8 :     if (qs < 0)
    5834             :     {
    5835           0 :       GNUNET_break (0);
    5836           0 :       postgres_rollback (pg);
    5837             :       return
    5838           0 :         TALER_EC_GENERIC_DB_STORE_FAILED;
    5839             :     }
    5840             :   }
    5841           8 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    5842             :                               tip_id,
    5843             :                               sizeof (*tip_id));
    5844             :   {
    5845           8 :     struct GNUNET_PQ_QueryParam params[] = {
    5846           8 :       GNUNET_PQ_query_param_string (instance_id),
    5847           8 :       GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
    5848           8 :       GNUNET_PQ_query_param_auto_from_type (tip_id),
    5849           8 :       GNUNET_PQ_query_param_string (justification),
    5850           8 :       GNUNET_PQ_query_param_string (next_url),
    5851           8 :       GNUNET_PQ_query_param_absolute_time (expiration),
    5852           8 :       TALER_PQ_query_param_amount (amount),
    5853             :       GNUNET_PQ_query_param_end
    5854             :     };
    5855             : 
    5856           8 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    5857             :                                              "insert_tip",
    5858             :                                              params);
    5859           8 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    5860             :     {
    5861           0 :       postgres_rollback (pg);
    5862           0 :       goto RETRY;
    5863             :     }
    5864           8 :     if (qs < 0)
    5865             :     {
    5866           0 :       GNUNET_break (0);
    5867           0 :       postgres_rollback (pg);
    5868           0 :       return TALER_EC_GENERIC_DB_STORE_FAILED;
    5869             :     }
    5870             :   }
    5871           8 :   qs = postgres_commit (pg);
    5872           8 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    5873           0 :     goto RETRY;
    5874           8 :   if (qs < 0)
    5875             :   {
    5876           0 :     GNUNET_break (0);
    5877           0 :     postgres_rollback (pg);
    5878           0 :     return TALER_EC_GENERIC_DB_COMMIT_FAILED;
    5879             :   }
    5880           8 :   return TALER_EC_NONE;
    5881             : }
    5882             : 
    5883             : 
    5884             : /**
    5885             :  * Closure for #lookup_signatures_cb().
    5886             :  */
    5887             : struct LookupSignaturesContext
    5888             : {
    5889             :   /**
    5890             :    * Length of the @e sigs array
    5891             :    */
    5892             :   unsigned int sigs_length;
    5893             : 
    5894             :   /**
    5895             :    * Where to store the signatures.
    5896             :    */
    5897             :   struct GNUNET_CRYPTO_RsaSignature **sigs;
    5898             : };
    5899             : 
    5900             : 
    5901             : /**
    5902             :  * Function to be called with the results of a SELECT statement
    5903             :  * that has returned @a num_results results about accounts.
    5904             :  *
    5905             :  * @param[in,out] cls of type `struct LookupSignaturesContext *`
    5906             :  * @param result the postgres result
    5907             :  * @param num_results the number of results in @a result
    5908             :  */
    5909             : static void
    5910           4 : lookup_signatures_cb (void *cls,
    5911             :                       PGresult *result,
    5912             :                       unsigned int num_results)
    5913             : {
    5914           4 :   struct LookupSignaturesContext *lsc = cls;
    5915             : 
    5916           7 :   for (unsigned int i = 0; i < num_results; i++)
    5917             :   {
    5918             :     uint32_t offset;
    5919             :     struct GNUNET_CRYPTO_RsaSignature *bsig;
    5920           3 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5921           3 :       GNUNET_PQ_result_spec_uint32 ("coin_offset",
    5922             :                                     &offset),
    5923           3 :       GNUNET_PQ_result_spec_rsa_signature ("blind_sig",
    5924             :                                            &bsig),
    5925             :       GNUNET_PQ_result_spec_end
    5926             :     };
    5927             : 
    5928           3 :     if (GNUNET_OK !=
    5929           3 :         GNUNET_PQ_extract_result (result,
    5930             :                                   rs,
    5931             :                                   i))
    5932             :     {
    5933           0 :       GNUNET_break (0);
    5934           0 :       return;
    5935             :     }
    5936           3 :     if (offset >= lsc->sigs_length)
    5937             :     {
    5938           0 :       GNUNET_break_op (0);
    5939           0 :       GNUNET_PQ_cleanup_result (rs);
    5940           0 :       continue;
    5941             :     }
    5942             :     /* Must be NULL due to UNIQUE constraint on offset and
    5943             :        requirement that client launched us with 'sigs'
    5944             :        pre-initialized to NULL. */
    5945           3 :     GNUNET_assert (NULL == lsc->sigs[offset]);
    5946           3 :     lsc->sigs[offset] = bsig;
    5947             :   }
    5948             : }
    5949             : 
    5950             : 
    5951             : /**
    5952             :  * Lookup pickup details for pickup @a pickup_id.
    5953             :  *
    5954             :  * @param cls closure, typically a connection to the db
    5955             :  * @param instance_id which instance should we lookup tip details for
    5956             :  * @param tip_id which tip should we lookup details on
    5957             :  * @param pickup_id which pickup should we lookup details on
    5958             :  * @param[out] exchange_url which exchange is the tip withdrawn from
    5959             :  * @param[out] reserve_priv private key the tip is withdrawn from (set if still available!)
    5960             :  * @param sigs_length length of the @a sigs array
    5961             :  * @param[out] sigs set to the (blind) signatures we have for this @a pickup_id,
    5962             :  *              those that are unavailable are left at NULL
    5963             :  * @return transaction status
    5964             :  */
    5965             : static enum GNUNET_DB_QueryStatus
    5966           8 : postgres_lookup_pickup (void *cls,
    5967             :                         const char *instance_id,
    5968             :                         const struct GNUNET_HashCode *tip_id,
    5969             :                         const struct GNUNET_HashCode *pickup_id,
    5970             :                         char **exchange_url,
    5971             :                         struct TALER_ReservePrivateKeyP *reserve_priv,
    5972             :                         unsigned int sigs_length,
    5973             :                         struct GNUNET_CRYPTO_RsaSignature *sigs[])
    5974             : {
    5975           8 :   struct PostgresClosure *pg = cls;
    5976             :   uint64_t pickup_serial;
    5977             : 
    5978             :   {
    5979           8 :     struct GNUNET_PQ_QueryParam params[] = {
    5980           8 :       GNUNET_PQ_query_param_string (instance_id),
    5981           8 :       GNUNET_PQ_query_param_auto_from_type (tip_id),
    5982           8 :       GNUNET_PQ_query_param_auto_from_type (pickup_id),
    5983             :       GNUNET_PQ_query_param_end
    5984             :     };
    5985           8 :     struct GNUNET_PQ_ResultSpec rs[] = {
    5986           8 :       GNUNET_PQ_result_spec_string ("exchange_url",
    5987             :                                     exchange_url),
    5988           8 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
    5989             :                                             reserve_priv),
    5990           8 :       GNUNET_PQ_result_spec_uint64 ("pickup_serial",
    5991             :                                     &pickup_serial),
    5992             :       GNUNET_PQ_result_spec_end
    5993             :     };
    5994             :     enum GNUNET_DB_QueryStatus qs;
    5995             : 
    5996           8 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    5997             :                                                    "lookup_pickup",
    5998             :                                                    params,
    5999             :                                                    rs);
    6000           8 :     if (qs <= 0)
    6001           4 :       return qs;
    6002             :   }
    6003             :   {
    6004           4 :     struct GNUNET_PQ_QueryParam params[] = {
    6005           4 :       GNUNET_PQ_query_param_uint64 (&pickup_serial),
    6006             :       GNUNET_PQ_query_param_end
    6007             :     };
    6008           4 :     struct LookupSignaturesContext lsc = {
    6009             :       .sigs_length = sigs_length,
    6010             :       .sigs = sigs
    6011             :     };
    6012             : 
    6013           4 :     return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    6014             :                                                  "lookup_pickup_signatures",
    6015             :                                                  params,
    6016             :                                                  &lookup_signatures_cb,
    6017             :                                                  &lsc);
    6018             :   }
    6019             : }
    6020             : 
    6021             : 
    6022             : /**
    6023             :  * Lookup tip details for tip @a tip_id.
    6024             :  *
    6025             :  * @param cls closure, typically a connection to the db
    6026             :  * @param instance_id which instance should we lookup tip details for
    6027             :  * @param tip_id which tip should we lookup details on
    6028             :  * @param[out] total_authorized amount how high is the tip (with fees)
    6029             :  * @param[out] total_picked_up how much of the tip was so far picked up (with fees)
    6030             :  * @param[out] expiration set to when the tip expires
    6031             :  * @param[out] exchange_url set to the exchange URL where the reserve is
    6032             :  * @param[out] reserve_priv set to private key of reserve to be debited
    6033             :  * @return transaction status
    6034             :  */
    6035             : static enum GNUNET_DB_QueryStatus
    6036          12 : postgres_lookup_tip (void *cls,
    6037             :                      const char *instance_id,
    6038             :                      const struct GNUNET_HashCode *tip_id,
    6039             :                      struct TALER_Amount *total_authorized,
    6040             :                      struct TALER_Amount *total_picked_up,
    6041             :                      struct GNUNET_TIME_Absolute *expiration,
    6042             :                      char **exchange_url,
    6043             :                      struct TALER_ReservePrivateKeyP *reserve_priv)
    6044             : {
    6045          12 :   struct PostgresClosure *pg = cls;
    6046          12 :   struct GNUNET_PQ_QueryParam params[] = {
    6047          12 :     GNUNET_PQ_query_param_string (instance_id),
    6048          12 :     GNUNET_PQ_query_param_auto_from_type (tip_id),
    6049             :     GNUNET_PQ_query_param_end
    6050             :   };
    6051          12 :   struct GNUNET_PQ_ResultSpec rs[] = {
    6052          12 :     TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    6053             :                                  total_authorized),
    6054          12 :     TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up",
    6055             :                                  total_picked_up),
    6056          12 :     GNUNET_PQ_result_spec_absolute_time ("expiration",
    6057             :                                          expiration),
    6058          12 :     GNUNET_PQ_result_spec_string ("exchange_url",
    6059             :                                   exchange_url),
    6060          12 :     GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
    6061             :                                           reserve_priv),
    6062             :     GNUNET_PQ_result_spec_end
    6063             :   };
    6064             : 
    6065          12 :   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6066             :                                                    "lookup_tip",
    6067             :                                                    params,
    6068             :                                                    rs);
    6069             : }
    6070             : 
    6071             : 
    6072             : /**
    6073             :  * Context used for postgres_lookup_tips().
    6074             :  */
    6075             : struct LookupMerchantTipsContext
    6076             : {
    6077             :   /**
    6078             :    * Postgres context.
    6079             :    */
    6080             :   struct PostgresClosure *pg;
    6081             : 
    6082             :   /**
    6083             :    * Function to call with the results.
    6084             :    */
    6085             :   TALER_MERCHANTDB_TipsCallback cb;
    6086             : 
    6087             :   /**
    6088             :    * Closure for @a cb.
    6089             :    */
    6090             :   void *cb_cls;
    6091             : 
    6092             :   /**
    6093             :    * Internal result.
    6094             :    */
    6095             :   enum GNUNET_DB_QueryStatus qs;
    6096             : };
    6097             : 
    6098             : 
    6099             : /**
    6100             :  * Function to be called with the results of a SELECT statement
    6101             :  * that has returned @a num_results results about tips.
    6102             :  *
    6103             :  * @param[in,out] cls of type `struct LookupTipsContext *`
    6104             :  * @param result the postgres result
    6105             :  * @param num_results the number of results in @a result
    6106             :  */
    6107             : static void
    6108          10 : lookup_tips_cb (void *cls,
    6109             :                 PGresult *result,
    6110             :                 unsigned int num_results)
    6111             : {
    6112          10 :   struct LookupMerchantTipsContext *plc = cls;
    6113          10 :   struct PostgresClosure *pg = plc->pg;
    6114             : 
    6115          40 :   for (unsigned int i = 0; i < num_results; i++)
    6116             :   {
    6117             :     uint64_t row_id;
    6118             :     struct GNUNET_HashCode tip_id;
    6119             :     struct TALER_Amount tip_amount;
    6120          30 :     struct GNUNET_PQ_ResultSpec rs[] = {
    6121          30 :       GNUNET_PQ_result_spec_uint64 ("tip_serial",
    6122             :                                     &row_id),
    6123          30 :       GNUNET_PQ_result_spec_auto_from_type ("tip_id",
    6124             :                                             &tip_id),
    6125          30 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    6126             :                                    &tip_amount),
    6127             :       GNUNET_PQ_result_spec_end
    6128             :     };
    6129             : 
    6130          30 :     if (GNUNET_OK !=
    6131          30 :         GNUNET_PQ_extract_result (result,
    6132             :                                   rs,
    6133             :                                   i))
    6134             :     {
    6135           0 :       GNUNET_break (0);
    6136           0 :       plc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    6137           0 :       return;
    6138             :     }
    6139          30 :     plc->cb (plc->cb_cls,
    6140             :              row_id,
    6141             :              tip_id,
    6142             :              tip_amount);
    6143          30 :     GNUNET_PQ_cleanup_result (rs);
    6144             :   }
    6145             : }
    6146             : 
    6147             : 
    6148             : /**
    6149             :  * Lookup tips
    6150             :  *
    6151             :  * @param cls closure, typically a connection to the db
    6152             :  * @param instance_id which instance should we lookup tips for
    6153             :  * @param expired should we include expired tips?
    6154             :  * @param limit maximum number of results to return, positive for
    6155             :  *   ascending row id, negative for descending
    6156             :  * @param offset row id to start returning results from
    6157             :  * @param cb function to call with tip data
    6158             :  * @param cb_cls closure for @a cb
    6159             :  * @return transaction status
    6160             :  */
    6161             : static enum GNUNET_DB_QueryStatus
    6162          10 : postgres_lookup_tips (void *cls,
    6163             :                       const char *instance_id,
    6164             :                       enum TALER_EXCHANGE_YesNoAll expired,
    6165             :                       int64_t limit,
    6166             :                       uint64_t offset,
    6167             :                       TALER_MERCHANTDB_TipsCallback cb,
    6168             :                       void *cb_cls)
    6169             : {
    6170          10 :   struct PostgresClosure *pg = cls;
    6171          10 :   struct LookupMerchantTipsContext plc = {
    6172             :     .pg = pg,
    6173             :     .cb = cb,
    6174             :     .cb_cls = cb_cls
    6175             :   };
    6176          10 :   uint64_t ulimit = (limit > 0) ? limit : -limit;
    6177             :   uint8_t bexpired;
    6178          10 :   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
    6179          10 :   struct GNUNET_PQ_QueryParam params[] = {
    6180          10 :     GNUNET_PQ_query_param_string (instance_id),
    6181          10 :     GNUNET_PQ_query_param_uint64 (&ulimit),
    6182          10 :     GNUNET_PQ_query_param_uint64 (&offset),
    6183          10 :     GNUNET_PQ_query_param_absolute_time (&now),
    6184          10 :     GNUNET_PQ_query_param_auto_from_type (&bexpired),
    6185             :     GNUNET_PQ_query_param_end
    6186             :   };
    6187             :   enum GNUNET_DB_QueryStatus qs;
    6188             :   char stmt[128];
    6189             : 
    6190          10 :   bexpired = (TALER_EXCHANGE_YNA_YES == expired);
    6191          10 :   GNUNET_snprintf (stmt,
    6192             :                    sizeof (stmt),
    6193             :                    "lookup_tips_%s%s",
    6194             :                    (limit > 0) ? "inc" : "dec",
    6195             :                    (TALER_EXCHANGE_YNA_ALL == expired) ? "" : "_expired");
    6196          10 :   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    6197             :                                              stmt,
    6198             :                                              params,
    6199             :                                              &lookup_tips_cb,
    6200             :                                              &plc);
    6201          10 :   if (0 != plc.qs)
    6202           0 :     return plc.qs;
    6203          10 :   return qs;
    6204             : }
    6205             : 
    6206             : 
    6207             : /**
    6208             :  * Closure for #lookup_pickup_details_cb().
    6209             :  */
    6210             : struct LookupTipDetailsContext
    6211             : {
    6212             :   /**
    6213             :    * Length of the @e sigs array
    6214             :    */
    6215             :   unsigned int *pickups_length;
    6216             : 
    6217             :   /**
    6218             :    * Where to store the signatures.
    6219             :    */
    6220             :   struct TALER_MERCHANTDB_PickupDetails **pickups;
    6221             : 
    6222             :   /**
    6223             :    * Database handle.
    6224             :    */
    6225             :   struct PostgresClosure *pg;
    6226             : 
    6227             :   /**
    6228             :    * Transaction status.
    6229             :    */
    6230             :   enum GNUNET_DB_QueryStatus qs;
    6231             : 
    6232             : };
    6233             : 
    6234             : 
    6235             : /**
    6236             :  * Function to be called with the results of a SELECT statement
    6237             :  * that has returned @a num_results results about pickups.
    6238             :  *
    6239             :  * @param[in,out] cls of type `struct LookupTipDetailsContext *`
    6240             :  * @param result the postgres result
    6241             :  * @param num_results the number of results in @a result
    6242             :  */
    6243             : static void
    6244           2 : lookup_pickup_details_cb (void *cls,
    6245             :                           PGresult *result,
    6246             :                           unsigned int num_results)
    6247             : {
    6248           2 :   struct LookupTipDetailsContext *ltdc = cls;
    6249           2 :   struct PostgresClosure *pg = ltdc->pg;
    6250             : 
    6251           2 :   *ltdc->pickups_length = num_results;
    6252           2 :   *ltdc->pickups = GNUNET_new_array (num_results,
    6253             :                                      struct TALER_MERCHANTDB_PickupDetails);
    6254           4 :   for (unsigned int i = 0; i < num_results; i++)
    6255             :   {
    6256           2 :     struct TALER_MERCHANTDB_PickupDetails *pd = &((*ltdc->pickups)[i]);
    6257           2 :     uint64_t num_planchets = 0;
    6258           2 :     struct GNUNET_PQ_ResultSpec rs[] = {
    6259           2 :       GNUNET_PQ_result_spec_auto_from_type ("pickup_id",
    6260             :                                             &pd->pickup_id),
    6261           2 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    6262             :                                    &pd->requested_amount),
    6263           2 :       GNUNET_PQ_result_spec_uint64 ("num_planchets",
    6264             :                                     &num_planchets),
    6265             :       GNUNET_PQ_result_spec_end
    6266             :     };
    6267             : 
    6268           2 :     if (GNUNET_OK !=
    6269           2 :         GNUNET_PQ_extract_result (result,
    6270             :                                   rs,
    6271             :                                   i))
    6272             :     {
    6273           0 :       GNUNET_break (0);
    6274           0 :       ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    6275           0 :       GNUNET_array_grow (*ltdc->pickups,
    6276             :                          *ltdc->pickups_length,
    6277             :                          0);
    6278           0 :       return;
    6279             :     }
    6280             : 
    6281           2 :     pd->num_planchets = num_planchets;
    6282             :   }
    6283             : }
    6284             : 
    6285             : 
    6286             : /**
    6287             :  * Lookup tip details for tip @a tip_id.
    6288             :  *
    6289             :  * @param cls closure, typically a connection to the db
    6290             :  * @param instance_id which instance should we lookup tip details for
    6291             :  * @param tip_id which tip should we lookup details on
    6292             :  * @param fpu should we fetch details about individual pickups
    6293             :  * @param[out] total_authorized amount how high is the tip (with fees)
    6294             :  * @param[out] total_picked_up how much of the tip was so far picked up (with fees)
    6295             :  * @param[out] justification why was the tip approved
    6296             :  * @param[out] expiration set to when the tip expires
    6297             :  * @param[out] reserve_pub set to which reserve is debited
    6298             :  * @param[out] pickups_length set to the length of @e pickups
    6299             :  * @param[out] pickups if @a fpu is true, set to details about the pickup operations
    6300             :  * @return transaction status,
    6301             :  */
    6302             : static enum GNUNET_DB_QueryStatus
    6303           3 : postgres_lookup_tip_details (void *cls,
    6304             :                              const char *instance_id,
    6305             :                              const struct GNUNET_HashCode *tip_id,
    6306             :                              bool fpu,
    6307             :                              struct TALER_Amount *total_authorized,
    6308             :                              struct TALER_Amount *total_picked_up,
    6309             :                              char **justification,
    6310             :                              struct GNUNET_TIME_Absolute *expiration,
    6311             :                              struct TALER_ReservePublicKeyP *reserve_pub,
    6312             :                              unsigned int *pickups_length,
    6313             :                              struct TALER_MERCHANTDB_PickupDetails **pickups)
    6314             : {
    6315           3 :   struct PostgresClosure *pg = cls;
    6316             :   uint64_t tip_serial;
    6317             :   enum GNUNET_DB_QueryStatus qs;
    6318             :   {
    6319           3 :     struct GNUNET_PQ_QueryParam params[] = {
    6320           3 :       GNUNET_PQ_query_param_string (instance_id),
    6321           3 :       GNUNET_PQ_query_param_auto_from_type (tip_id),
    6322             :       GNUNET_PQ_query_param_end
    6323             :     };
    6324           3 :     struct GNUNET_PQ_ResultSpec rs[] = {
    6325           3 :       GNUNET_PQ_result_spec_uint64 ("tip_serial",
    6326             :                                     &tip_serial),
    6327           3 :       TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
    6328             :                                    total_authorized),
    6329           3 :       TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up",
    6330             :                                    total_picked_up),
    6331           3 :       GNUNET_PQ_result_spec_string ("justification",
    6332             :                                     justification),
    6333           3 :       GNUNET_PQ_result_spec_absolute_time ("expiration",
    6334             :                                            expiration),
    6335           3 :       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
    6336             :                                             reserve_pub),
    6337             :       GNUNET_PQ_result_spec_end
    6338             :     };
    6339             : 
    6340           3 :     check_connection (pg);
    6341           3 :     qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6342             :                                                    "lookup_tip_details",
    6343             :                                                    params,
    6344             :                                                    rs);
    6345           3 :     if (qs <= 0)
    6346           1 :       return qs;
    6347           3 :     if (! fpu)
    6348             :     {
    6349           1 :       *pickups_length = 0;
    6350           1 :       *pickups = NULL;
    6351           1 :       return qs;
    6352             :     }
    6353             :   }
    6354             :   {
    6355           2 :     struct GNUNET_PQ_QueryParam params[] = {
    6356           2 :       GNUNET_PQ_query_param_uint64 (&tip_serial),
    6357             :       GNUNET_PQ_query_param_end
    6358             :     };
    6359             : 
    6360           2 :     struct LookupTipDetailsContext ltdc = {
    6361             :       .pickups_length = pickups_length,
    6362             :       .pickups = pickups,
    6363             :       .pg = pg,
    6364             :       .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    6365             :     };
    6366             : 
    6367           2 :     qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
    6368             :                                                "lookup_pickup_details",
    6369             :                                                params,
    6370             :                                                &lookup_pickup_details_cb,
    6371             :                                                &ltdc);
    6372           2 :     if (qs < 0)
    6373           0 :       return qs;
    6374           2 :     return ltdc.qs;
    6375             :   }
    6376             : }
    6377             : 
    6378             : 
    6379             : /**
    6380             :  * Insert details about a tip pickup operation.  The @a total_picked_up
    6381             :  * UPDATES the total amount under the @a tip_id, while the @a
    6382             :  * total_requested is the amount to be associated with this @a pickup_id.
    6383             :  * While there is usually only one pickup event that picks up the entire
    6384             :  * amount, our schema allows for wallets to pick up the amount incrementally
    6385             :  * over multiple pick up operations.
    6386             :  *
    6387             :  * @param cls closure, typically a connection to the db
    6388             :  * @param instance_id which instance gave the tip
    6389             :  * @param tip_id the unique ID for the tip
    6390             :  * @param total_picked_up how much was picked up overall at this
    6391             :  *          point (includes @a total_requested)
    6392             :  * @param pickup_id unique ID for the operation
    6393             :  * @param total_requested how much is being picked up in this operation
    6394             :  * @return transaction status, usually
    6395             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    6396             :  *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
    6397             :  */
    6398             : static enum GNUNET_DB_QueryStatus
    6399           4 : postgres_insert_pickup (void *cls,
    6400             :                         const char *instance_id,
    6401             :                         const struct GNUNET_HashCode *tip_id,
    6402             :                         const struct TALER_Amount *total_picked_up,
    6403             :                         const struct GNUNET_HashCode *pickup_id,
    6404             :                         const struct TALER_Amount *total_requested)
    6405             : {
    6406           4 :   struct PostgresClosure *pg = cls;
    6407             :   enum GNUNET_DB_QueryStatus qs;
    6408             : 
    6409             :   {
    6410           4 :     struct GNUNET_PQ_QueryParam params[] = {
    6411           4 :       GNUNET_PQ_query_param_string (instance_id),
    6412           4 :       GNUNET_PQ_query_param_auto_from_type (tip_id),
    6413           4 :       GNUNET_PQ_query_param_auto_from_type (pickup_id),
    6414           4 :       TALER_PQ_query_param_amount (total_requested),
    6415             :       GNUNET_PQ_query_param_end
    6416             :     };
    6417             : 
    6418           4 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6419             :                                              "insert_pickup",
    6420             :                                              params);
    6421           4 :     if (0 > qs)
    6422           0 :       return qs;
    6423             :   }
    6424             : 
    6425             :   {
    6426           4 :     struct GNUNET_PQ_QueryParam params[] = {
    6427           4 :       GNUNET_PQ_query_param_auto_from_type (tip_id),
    6428           4 :       TALER_PQ_query_param_amount (total_picked_up),
    6429             :       GNUNET_PQ_query_param_end
    6430             :     };
    6431             : 
    6432           4 :     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6433             :                                              "update_picked_up_tip",
    6434             :                                              params);
    6435           4 :     if (0 > qs)
    6436           0 :       return qs;
    6437             :   }
    6438             :   {
    6439             :     uint64_t reserve_serial;
    6440             :     struct TALER_Amount reserve_picked_up;
    6441             :     {
    6442           4 :       struct GNUNET_PQ_QueryParam params[] = {
    6443           4 :         GNUNET_PQ_query_param_string (instance_id),
    6444           4 :         GNUNET_PQ_query_param_auto_from_type (tip_id),
    6445             :         GNUNET_PQ_query_param_end
    6446             :       };
    6447           4 :       struct GNUNET_PQ_ResultSpec rs[] = {
    6448           4 :         GNUNET_PQ_result_spec_uint64 ("reserve_serial",
    6449             :                                       &reserve_serial),
    6450           4 :         TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
    6451             :                                      &reserve_picked_up),
    6452             :         GNUNET_PQ_result_spec_end
    6453             : 
    6454             :       };
    6455             : 
    6456           4 :       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    6457             :                                                      "lookup_picked_up_reserve",
    6458             :                                                      params,
    6459             :                                                      rs);
    6460           4 :       if (0 > qs)
    6461           0 :         return qs;
    6462             :     }
    6463           4 :     if (0 >=
    6464           4 :         TALER_amount_add (&reserve_picked_up,
    6465             :                           &reserve_picked_up,
    6466             :                           total_requested))
    6467             :     {
    6468           0 :       GNUNET_break (0);
    6469           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    6470             :     }
    6471             : 
    6472             :     {
    6473           4 :       struct GNUNET_PQ_QueryParam params[] = {
    6474           4 :         GNUNET_PQ_query_param_uint64 (&reserve_serial),
    6475           4 :         TALER_PQ_query_param_amount (&reserve_picked_up),
    6476             :         GNUNET_PQ_query_param_end
    6477             :       };
    6478             : 
    6479           4 :       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6480             :                                                "update_picked_up_reserve",
    6481             :                                                params);
    6482           4 :       if (0 > qs)
    6483           0 :         return qs;
    6484             :     }
    6485             :   }
    6486           4 :   return qs;
    6487             : }
    6488             : 
    6489             : 
    6490             : /**
    6491             :  * Insert blind signature obtained from the exchange during a
    6492             :  * tip pickup operation.
    6493             :  *
    6494             :  * @param cls closure, typically a connection to the db
    6495             :  * @param pickup_id unique ID for the operation
    6496             :  * @param offset offset of the blind signature for the pickup
    6497             :  * @param blind_sig the blind signature
    6498             :  * @return transaction status, usually
    6499             :  *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
    6500             :  *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
    6501             :  */
    6502             : static enum GNUNET_DB_QueryStatus
    6503           4 : postgres_insert_pickup_blind_signature (
    6504             :   void *cls,
    6505             :   const struct GNUNET_HashCode *pickup_id,
    6506             :   uint32_t offset,
    6507             :   const struct GNUNET_CRYPTO_RsaSignature *blind_sig)
    6508             : {
    6509           4 :   struct PostgresClosure *pg = cls;
    6510           4 :   struct GNUNET_PQ_QueryParam params[] = {
    6511           4 :     GNUNET_PQ_query_param_auto_from_type (pickup_id),
    6512           4 :     GNUNET_PQ_query_param_uint32 (&offset),
    6513           4 :     GNUNET_PQ_query_param_rsa_signature (blind_sig),
    6514             :     GNUNET_PQ_query_param_end
    6515             :   };
    6516             : 
    6517           4 :   check_connection (pg);
    6518           4 :   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    6519             :                                              "insert_pickup_blind_signature",
    6520             :                                              params);
    6521             : }
    6522             : 
    6523             : 
    6524             : /**
    6525             :  * Establish connection to the database.
    6526             :  *
    6527             :  * @param cls plugin context
    6528             :  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
    6529             :  */
    6530             : static int
    6531          17 : postgres_connect (void *cls)
    6532             : {
    6533          17 :   struct PostgresClosure *pg = cls;
    6534          17 :   struct GNUNET_PQ_PreparedStatement ps[] = {
    6535          17 :     GNUNET_PQ_make_prepare ("end_transaction",
    6536             :                             "COMMIT",
    6537             :                             0),
    6538             :     /* for call_with_accounts(), part of postgres_lookup_instances() */
    6539          17 :     GNUNET_PQ_make_prepare ("lookup_instance_private_key",
    6540             :                             "SELECT"
    6541             :                             " merchant_priv"
    6542             :                             " FROM merchant_keys"
    6543             :                             " WHERE merchant_serial=$1",
    6544             :                             1),
    6545             :     /* for find_instances_cb(), part of postgres_lookup_instances() */
    6546          17 :     GNUNET_PQ_make_prepare ("lookup_accounts",
    6547             :                             "SELECT"
    6548             :                             " h_wire"
    6549             :                             ",salt"
    6550             :                             ",payto_uri"
    6551             :                             ",active"
    6552             :                             " FROM merchant_accounts"
    6553             :                             " WHERE merchant_serial=$1",
    6554             :                             1),
    6555             :     /* for postgres_lookup_instances() */
    6556          17 :     GNUNET_PQ_make_prepare ("lookup_instance_auth",
    6557             :                             "SELECT"
    6558             :                             " auth_hash"
    6559             :                             ",auth_salt"
    6560             :                             " FROM merchant_instances"
    6561             :                             " WHERE merchant_id=$1",
    6562             :                             1),
    6563             :     /* for postgres_lookup_instances() */
    6564          17 :     GNUNET_PQ_make_prepare ("lookup_instances",
    6565             :                             "SELECT"
    6566             :                             " merchant_serial"
    6567             :                             ",merchant_pub"
    6568             :                             ",auth_hash"
    6569             :                             ",auth_salt"
    6570             :                             ",merchant_id"
    6571             :                             ",merchant_name"
    6572             :                             ",address"
    6573             :                             ",jurisdiction"
    6574             :                             ",default_max_deposit_fee_val"
    6575             :                             ",default_max_deposit_fee_frac"
    6576             :                             ",default_max_wire_fee_val"
    6577             :                             ",default_max_wire_fee_frac"
    6578             :                             ",default_wire_fee_amortization"
    6579             :                             ",default_wire_transfer_delay"
    6580             :                             ",default_pay_delay"
    6581             :                             " FROM merchant_instances",
    6582             :                             0),
    6583             :     /* for postgres_lookup_instance() */
    6584          17 :     GNUNET_PQ_make_prepare ("lookup_instance",
    6585             :                             "SELECT"
    6586             :                             " merchant_serial"
    6587             :                             ",merchant_pub"
    6588             :                             ",auth_hash"
    6589             :                             ",auth_salt"
    6590             :                             ",merchant_id"
    6591             :                             ",merchant_name"
    6592             :                             ",address"
    6593             :                             ",jurisdiction"
    6594             :                             ",default_max_deposit_fee_val"
    6595             :                             ",default_max_deposit_fee_frac"
    6596             :                             ",default_max_wire_fee_val"
    6597             :                             ",default_max_wire_fee_frac"
    6598             :                             ",default_wire_fee_amortization"
    6599             :                             ",default_wire_transfer_delay"
    6600             :                             ",default_pay_delay"
    6601             :                             " FROM merchant_instances"
    6602             :                             " WHERE merchant_id=$1",
    6603             :                             1),
    6604             :     /* for postgres_insert_instance() */
    6605          17 :     GNUNET_PQ_make_prepare ("insert_instance",
    6606             :                             "INSERT INTO merchant_instances"
    6607             :                             "(merchant_pub"
    6608             :                             ",auth_hash"
    6609             :                             ",auth_salt"
    6610             :                             ",merchant_id"
    6611             :                             ",merchant_name"
    6612             :                             ",address"
    6613             :                             ",jurisdiction"
    6614             :                             ",default_max_deposit_fee_val"
    6615             :                             ",default_max_deposit_fee_frac"
    6616             :                             ",default_max_wire_fee_val"
    6617             :                             ",default_max_wire_fee_frac"
    6618             :                             ",default_wire_fee_amortization"
    6619             :                             ",default_wire_transfer_delay"
    6620             :                             ",default_pay_delay)"
    6621             :                             "VALUES"
    6622             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)",
    6623             :                             14),
    6624             :     /* for postgres_insert_instance() */
    6625          17 :     GNUNET_PQ_make_prepare ("insert_keys",
    6626             :                             "INSERT INTO merchant_keys"
    6627             :                             "(merchant_priv"
    6628             :                             ",merchant_serial)"
    6629             :                             " SELECT $1, merchant_serial"
    6630             :                             " FROM merchant_instances"
    6631             :                             " WHERE merchant_id=$2",
    6632             :                             2),
    6633             :     /* for postgres_insert_account() */
    6634          17 :     GNUNET_PQ_make_prepare ("insert_account",
    6635             :                             "INSERT INTO merchant_accounts"
    6636             :                             "(merchant_serial"
    6637             :                             ",h_wire"
    6638             :                             ",salt"
    6639             :                             ",payto_uri"
    6640             :                             ",active)"
    6641             :                             " SELECT merchant_serial, $2, $3, $4, $5"
    6642             :                             " FROM merchant_instances"
    6643             :                             " WHERE merchant_id=$1",
    6644             :                             5),
    6645             :     /* for postgres_delete_instance_private_key() */
    6646          17 :     GNUNET_PQ_make_prepare ("delete_key",
    6647             :                             "DELETE FROM merchant_keys"
    6648             :                             " USING merchant_instances"
    6649             :                             " WHERE merchant_keys.merchant_serial"
    6650             :                             "   = merchant_instances.merchant_serial"
    6651             :                             " AND merchant_instances.merchant_id = $1",
    6652             :                             1),
    6653             :     /* for postgres_purge_instance() */
    6654          17 :     GNUNET_PQ_make_prepare ("purge_instance",
    6655             :                             "DELETE FROM merchant_instances"
    6656             :                             " WHERE merchant_instances.merchant_id = $1",
    6657             :                             1),
    6658             :     /* for postgres_update_instance() */
    6659          17 :     GNUNET_PQ_make_prepare ("update_instance",
    6660             :                             "UPDATE merchant_instances SET"
    6661             :                             " merchant_name=$2"
    6662             :                             ",address=$3"
    6663             :                             ",jurisdiction=$4"
    6664             :                             ",default_max_deposit_fee_val=$5"
    6665             :                             ",default_max_deposit_fee_frac=$6"
    6666             :                             ",default_max_wire_fee_val=$7"
    6667             :                             ",default_max_wire_fee_frac=$8"
    6668             :                             ",default_wire_fee_amortization=$9"
    6669             :                             ",default_wire_transfer_delay=$10"
    6670             :                             ",default_pay_delay=$11"
    6671             :                             " WHERE merchant_id = $1",
    6672             :                             11),
    6673             :     /* for postgres_update_instance_auth() */
    6674          17 :     GNUNET_PQ_make_prepare ("update_instance_auth",
    6675             :                             "UPDATE merchant_instances SET"
    6676             :                             " auth_hash=$2"
    6677             :                             ",auth_salt=$3"
    6678             :                             " WHERE merchant_id=$1",
    6679             :                             3),
    6680             :     /* for postgres_inactivate_account(); the merchant
    6681             :        instance is implied from the random salt that
    6682             :        is part of the h_wire calculation */
    6683          17 :     GNUNET_PQ_make_prepare ("inactivate_account",
    6684             :                             "UPDATE merchant_accounts SET"
    6685             :                             " active=FALSE"
    6686             :                             " WHERE h_wire=$2 AND"
    6687             :                             " merchant_serial="
    6688             :                             "   (SELECT merchant_serial"
    6689             :                             "      FROM merchant_instances"
    6690             :                             "      WHERE merchant_id=$1)",
    6691             :                             2),
    6692             :     /* for postgres_activate_account() */
    6693          17 :     GNUNET_PQ_make_prepare ("activate_account",
    6694             :                             "UPDATE merchant_accounts SET"
    6695             :                             " active=TRUE"
    6696             :                             " WHERE h_wire=$2 AND"
    6697             :                             " merchant_serial="
    6698             :                             "   (SELECT merchant_serial"
    6699             :                             "      FROM merchant_instances"
    6700             :                             "      WHERE merchant_id=$1)",
    6701             :                             2),
    6702             :     /* for postgres_lookup_products() */
    6703          17 :     GNUNET_PQ_make_prepare ("lookup_products",
    6704             :                             "SELECT"
    6705             :                             " product_id"
    6706             :                             " FROM merchant_inventory"
    6707             :                             " JOIN merchant_instances"
    6708             :                             "   USING (merchant_serial)"
    6709             :                             " WHERE merchant_instances.merchant_id=$1",
    6710             :                             1),
    6711             :     /* for postgres_lookup_product() */
    6712          17 :     GNUNET_PQ_make_prepare ("lookup_product",
    6713             :                             "SELECT"
    6714             :                             " description"
    6715             :                             ",description_i18n"
    6716             :                             ",unit"
    6717             :                             ",price_val"
    6718             :                             ",price_frac"
    6719             :                             ",taxes"
    6720             :                             ",total_stock"
    6721             :                             ",total_sold"
    6722             :                             ",total_lost"
    6723             :                             ",image"
    6724             :                             ",merchant_inventory.address"
    6725             :                             ",next_restock"
    6726             :                             " FROM merchant_inventory"
    6727             :                             " JOIN merchant_instances"
    6728             :                             "   USING (merchant_serial)"
    6729             :                             " WHERE merchant_instances.merchant_id=$1"
    6730             :                             "   AND merchant_inventory.product_id=$2",
    6731             :                             2),
    6732             :     /* for postgres_delete_product() */
    6733          17 :     GNUNET_PQ_make_prepare ("delete_product",
    6734             :                             "DELETE"
    6735             :                             " FROM merchant_inventory"
    6736             :                             " WHERE merchant_inventory.merchant_serial="
    6737             :                             "     (SELECT merchant_serial "
    6738             :                             "        FROM merchant_instances"
    6739             :                             "        WHERE merchant_id=$1)"
    6740             :                             "   AND merchant_inventory.product_id=$2"
    6741             :                             "   AND product_serial NOT IN "
    6742             :                             "     (SELECT product_serial FROM merchant_order_locks)"
    6743             :                             "   AND product_serial NOT IN "
    6744             :                             "     (SELECT product_serial FROM merchant_inventory_locks)",
    6745             :                             2),
    6746             :     /* for postgres_insert_product() */
    6747          17 :     GNUNET_PQ_make_prepare ("insert_product",
    6748             :                             "INSERT INTO merchant_inventory"
    6749             :                             "(merchant_serial"
    6750             :                             ",product_id"
    6751             :                             ",description"
    6752             :                             ",description_i18n"
    6753             :                             ",unit"
    6754             :                             ",image"
    6755             :                             ",taxes"
    6756             :                             ",price_val"
    6757             :                             ",price_frac"
    6758             :                             ",total_stock"
    6759             :                             ",address"
    6760             :                             ",next_restock)"
    6761             :                             " SELECT merchant_serial,"
    6762             :                             " $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12"
    6763             :                             " FROM merchant_instances"
    6764             :                             " WHERE merchant_id=$1",
    6765             :                             12),
    6766             :     /* for postgres_update_product() */
    6767          17 :     GNUNET_PQ_make_prepare ("update_product",
    6768             :                             "UPDATE merchant_inventory SET"
    6769             :                             " description=$3"
    6770             :                             ",description_i18n=$4"
    6771             :                             ",unit=$5"
    6772             :                             ",image=$6"
    6773             :                             ",taxes=$7"
    6774             :                             ",price_val=$8"
    6775             :                             ",price_frac=$9"
    6776             :                             ",total_stock=$10"
    6777             :                             ",total_lost=$11"
    6778             :                             ",address=$12"
    6779             :                             ",next_restock=$13"
    6780             :                             " WHERE merchant_serial="
    6781             :                             "   (SELECT merchant_serial"
    6782             :                             "      FROM merchant_instances"
    6783             :                             "      WHERE merchant_id=$1)"
    6784             :                             "   AND product_id=$2"
    6785             :                             "   AND total_stock <= $10"
    6786             :                             "   AND total_lost <= $11",
    6787             :                             13),
    6788             : 
    6789             :     /* for postgres_lock_product() */
    6790          17 :     GNUNET_PQ_make_prepare ("lock_product",
    6791             :                             "WITH ps AS"
    6792             :                             "  (SELECT product_serial"
    6793             :                             "   FROM merchant_inventory"
    6794             :                             "   WHERE product_id=$2"
    6795             :                             "     AND merchant_serial="
    6796             :                             "     (SELECT merchant_serial"
    6797             :                             "        FROM merchant_instances"
    6798             :                             "        WHERE merchant_id=$1))"
    6799             :                             "INSERT INTO merchant_inventory_locks"
    6800             :                             "(product_serial"
    6801             :                             ",lock_uuid"
    6802             :                             ",total_locked"
    6803             :                             ",expiration)"
    6804             :                             " SELECT product_serial, $3, $4, $5"
    6805             :                             "   FROM merchant_inventory"
    6806             :                             "   JOIN ps USING (product_serial)"
    6807             :                             "   WHERE "
    6808             :                             "     total_stock - total_sold - total_lost - $4 >= "
    6809             :                             "     (SELECT COALESCE(SUM(total_locked), 0)"
    6810             :                             "        FROM merchant_inventory_locks"
    6811             :                             "        WHERE product_serial=ps.product_serial) + "
    6812             :                             "     (SELECT COALESCE(SUM(total_locked), 0)"
    6813             :                             "        FROM merchant_order_locks"
    6814             :                             "        WHERE product_serial=ps.product_serial)",
    6815             :                             5),
    6816             : 
    6817             :     /* for postgres_expire_locks() */
    6818          17 :     GNUNET_PQ_make_prepare ("unlock_products",
    6819             :                             "DELETE FROM merchant_inventory_locks"
    6820             :                             " WHERE expiration < $1",
    6821             :                             1),
    6822             :     /* for postgres_expire_locks() */
    6823          17 :     GNUNET_PQ_make_prepare ("unlock_orders",
    6824             :                             "DELETE FROM merchant_orders"
    6825             :                             " WHERE pay_deadline < $1",
    6826             :                             1),
    6827             : 
    6828             :     /* for postgres_delete_order() */
    6829          17 :     GNUNET_PQ_make_prepare ("delete_order",
    6830             :                             "DELETE"
    6831             :                             " FROM merchant_orders"
    6832             :                             " WHERE merchant_orders.merchant_serial="
    6833             :                             "     (SELECT merchant_serial "
    6834             :                             "        FROM merchant_instances"
    6835             :                             "        WHERE merchant_id=$1)"
    6836             :                             "   AND merchant_orders.order_id=$2"
    6837             :                             "   AND"
    6838             :                             "       ((NOT EXISTS"
    6839             :                             "        (SELECT order_id"
    6840             :                             "           FROM merchant_contract_terms"
    6841             :                             "           WHERE merchant_contract_terms.order_id=$2))"
    6842             :                             "   OR pay_deadline < $3)",
    6843             :                             3),
    6844             :     /* for postgres_lookup_order() */
    6845          17 :     GNUNET_PQ_make_prepare ("lookup_order",
    6846             :                             "SELECT"
    6847             :                             " contract_terms"
    6848             :                             ",claim_token"
    6849             :                             ",h_post_data"
    6850             :                             " FROM merchant_orders"
    6851             :                             " WHERE merchant_orders.merchant_serial="
    6852             :                             "     (SELECT merchant_serial "
    6853             :                             "        FROM merchant_instances"
    6854             :                             "        WHERE merchant_id=$1)"
    6855             :                             "   AND merchant_orders.order_id=$2",
    6856             :                             2),
    6857             :     /* for postgres_lookup_order_summary() */
    6858          17 :     GNUNET_PQ_make_prepare ("lookup_order_summary",
    6859             :                             "(SELECT"
    6860             :                             " creation_time"
    6861             :                             ",order_serial"
    6862             :                             " FROM merchant_contract_terms"
    6863             :                             " WHERE merchant_contract_terms.merchant_serial="
    6864             :                             "     (SELECT merchant_serial "
    6865             :                             "        FROM merchant_instances"
    6866             :                             "        WHERE merchant_id=$1)"
    6867             :                             "   AND merchant_contract_terms.order_id=$2)"
    6868             :                             "UNION"
    6869             :                             "(SELECT"
    6870             :                             " creation_time"
    6871             :                             ",order_serial"
    6872             :                             " FROM merchant_orders"
    6873             :                             " WHERE merchant_orders.merchant_serial="
    6874             :                             "     (SELECT merchant_serial "
    6875             :                             "        FROM merchant_instances"
    6876             :                             "        WHERE merchant_id=$1)"
    6877             :                             "   AND merchant_orders.order_id=$2)",
    6878             :                             2),
    6879             :     /* for postgres_lookup_orders() */
    6880          17 :     GNUNET_PQ_make_prepare ("lookup_orders_inc",
    6881             :                             "(SELECT"
    6882             :                             " order_id"
    6883             :                             ",order_serial"
    6884             :                             ",creation_time"
    6885             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    6886             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    6887             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    6888             :                             " FROM merchant_orders"
    6889             :                             " WHERE merchant_orders.merchant_serial="
    6890             :                             "     (SELECT merchant_serial "
    6891             :                             "        FROM merchant_instances"
    6892             :                             "        WHERE merchant_id=$1)"
    6893             :                             "   AND"
    6894             :                             "    order_serial > $3"
    6895             :                             "   AND"
    6896             :                             "    creation_time > $4"
    6897             :                             " ORDER BY order_serial ASC"
    6898             :                             " LIMIT $2)"
    6899             :                             "UNION " /* union ensures elements are distinct! */
    6900             :                             "(SELECT"
    6901             :                             " order_id"
    6902             :                             ",order_serial"
    6903             :                             ",creation_time"
    6904             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    6905             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    6906             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    6907             :                             " FROM merchant_contract_terms"
    6908             :                             " WHERE merchant_contract_terms.merchant_serial="
    6909             :                             "     (SELECT merchant_serial "
    6910             :                             "        FROM merchant_instances"
    6911             :                             "        WHERE merchant_id=$1)"
    6912             :                             "   AND"
    6913             :                             "    order_serial > $3"
    6914             :                             "   AND"
    6915             :                             "    creation_time > $4"
    6916             :                             " ORDER BY order_serial ASC"
    6917             :                             " LIMIT $2)"
    6918             :                             " ORDER BY order_serial ASC"
    6919             :                             " LIMIT $2",
    6920             :                             7),
    6921          17 :     GNUNET_PQ_make_prepare ("lookup_orders_inc_paid",
    6922             :                             "(SELECT"
    6923             :                             " order_id"
    6924             :                             ",order_serial"
    6925             :                             ",creation_time"
    6926             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    6927             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    6928             :                             " FROM merchant_orders"
    6929             :                             " WHERE merchant_orders.merchant_serial="
    6930             :                             "     (SELECT merchant_serial "
    6931             :                             "        FROM merchant_instances"
    6932             :                             "        WHERE merchant_id=$1)"
    6933             :                             "   AND"
    6934             :                             "    order_serial > $3"
    6935             :                             "   AND"
    6936             :                             "    creation_time > $4"
    6937             :                             "   AND"
    6938             :                             "    NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
    6939             :                             "   AND"
    6940             :                             "    order_serial NOT IN"
    6941             :                             "     (SELECT order_serial"
    6942             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    6943             :                             " ORDER BY order_serial ASC"
    6944             :                             " LIMIT $2)"
    6945             :                             "UNION " /* union ensures elements are distinct! */
    6946             :                             "(SELECT"
    6947             :                             " order_id"
    6948             :                             ",order_serial"
    6949             :                             ",creation_time"
    6950             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    6951             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    6952             :                             " FROM merchant_contract_terms"
    6953             :                             " WHERE merchant_contract_terms.merchant_serial="
    6954             :                             "     (SELECT merchant_serial "
    6955             :                             "        FROM merchant_instances"
    6956             :                             "        WHERE merchant_id=$1)"
    6957             :                             "   AND"
    6958             :                             "    order_serial > $3"
    6959             :                             "   AND"
    6960             :                             "    creation_time > $4"
    6961             :                             "   AND"
    6962             :                             "    BOOL($5) = paid"
    6963             :                             " ORDER BY order_serial ASC"
    6964             :                             " LIMIT $2)"
    6965             :                             " ORDER BY order_serial ASC"
    6966             :                             " LIMIT $2",
    6967             :                             7),
    6968          17 :     GNUNET_PQ_make_prepare ("lookup_orders_inc_refunded",
    6969             :                             "(SELECT"
    6970             :                             " order_id"
    6971             :                             ",order_serial"
    6972             :                             ",creation_time"
    6973             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    6974             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    6975             :                             " FROM merchant_orders"
    6976             :                             " WHERE merchant_orders.merchant_serial="
    6977             :                             "     (SELECT merchant_serial "
    6978             :                             "        FROM merchant_instances"
    6979             :                             "        WHERE merchant_id=$1)"
    6980             :                             "   AND"
    6981             :                             "    order_serial > $3"
    6982             :                             "   AND"
    6983             :                             "    creation_time > $4"
    6984             :                             "   AND"
    6985             :                             "    NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
    6986             :                             "   AND"
    6987             :                             "    order_serial NOT IN"
    6988             :                             "     (SELECT order_serial"
    6989             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    6990             :                             " ORDER BY order_serial ASC"
    6991             :                             " LIMIT $2)"
    6992             :                             "UNION " /* union ensures elements are distinct! */
    6993             :                             "(SELECT"
    6994             :                             " order_id"
    6995             :                             ",order_serial"
    6996             :                             ",creation_time"
    6997             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    6998             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    6999             :                             " FROM merchant_contract_terms"
    7000             :                             " WHERE merchant_contract_terms.merchant_serial="
    7001             :                             "     (SELECT merchant_serial "
    7002             :                             "        FROM merchant_instances"
    7003             :                             "        WHERE merchant_id=$1)"
    7004             :                             "   AND"
    7005             :                             "    order_serial > $3"
    7006             :                             "   AND"
    7007             :                             "    creation_time > $4"
    7008             :                             "   AND"
    7009             :                             "    CAST($6 as BOOL) = (order_serial IN"
    7010             :                             "     (SELECT order_serial "
    7011             :                             "      FROM merchant_refunds))"
    7012             :                             " ORDER BY order_serial ASC"
    7013             :                             " LIMIT $2)"
    7014             :                             " ORDER BY order_serial ASC"
    7015             :                             " LIMIT $2",
    7016             :                             7),
    7017          17 :     GNUNET_PQ_make_prepare ("lookup_orders_inc_wired",
    7018             :                             "(SELECT"
    7019             :                             " order_id"
    7020             :                             ",order_serial"
    7021             :                             ",creation_time"
    7022             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7023             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7024             :                             " FROM merchant_orders"
    7025             :                             " WHERE merchant_orders.merchant_serial="
    7026             :                             "     (SELECT merchant_serial "
    7027             :                             "        FROM merchant_instances"
    7028             :                             "        WHERE merchant_id=$1)"
    7029             :                             "   AND"
    7030             :                             "    order_serial > $3"
    7031             :                             "   AND"
    7032             :                             "    creation_time > $4"
    7033             :                             "   AND"
    7034             :                             "    NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
    7035             :                             "   AND"
    7036             :                             "    order_serial NOT IN"
    7037             :                             "     (SELECT order_serial"
    7038             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7039             :                             " ORDER BY order_serial ASC"
    7040             :                             " LIMIT $2)"
    7041             :                             "UNION " /* union ensures elements are distinct! */
    7042             :                             "(SELECT"
    7043             :                             " order_id"
    7044             :                             ",order_serial"
    7045             :                             ",creation_time"
    7046             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7047             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7048             :                             " FROM merchant_contract_terms"
    7049             :                             " WHERE merchant_contract_terms.merchant_serial="
    7050             :                             "     (SELECT merchant_serial "
    7051             :                             "        FROM merchant_instances"
    7052             :                             "        WHERE merchant_id=$1)"
    7053             :                             "   AND"
    7054             :                             "    order_serial > $3"
    7055             :                             "   AND"
    7056             :                             "    creation_time > $4"
    7057             :                             "   AND"
    7058             :                             "    BOOL($7) = wired"
    7059             :                             " ORDER BY order_serial ASC"
    7060             :                             " LIMIT $2)"
    7061             :                             " ORDER BY order_serial ASC"
    7062             :                             " LIMIT $2",
    7063             :                             7),
    7064          17 :     GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_refunded",
    7065             :                             "(SELECT"
    7066             :                             " order_id"
    7067             :                             ",order_serial"
    7068             :                             ",creation_time"
    7069             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7070             :                             " FROM merchant_orders"
    7071             :                             " WHERE merchant_orders.merchant_serial="
    7072             :                             "     (SELECT merchant_serial "
    7073             :                             "        FROM merchant_instances"
    7074             :                             "        WHERE merchant_id=$1)"
    7075             :                             "   AND"
    7076             :                             "    order_serial > $3"
    7077             :                             "   AND"
    7078             :                             "    creation_time > $4"
    7079             :                             "   AND"
    7080             :                             "    NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
    7081             :                             "   AND"
    7082             :                             "    NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
    7083             :                             "   AND"
    7084             :                             "    order_serial NOT IN"
    7085             :                             "     (SELECT order_serial"
    7086             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7087             :                             " ORDER BY order_serial ASC"
    7088             :                             " LIMIT $2)"
    7089             :                             "UNION " /* union ensures elements are distinct! */
    7090             :                             "(SELECT"
    7091             :                             " order_id"
    7092             :                             ",order_serial"
    7093             :                             ",creation_time"
    7094             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7095             :                             " FROM merchant_contract_terms"
    7096             :                             " WHERE merchant_contract_terms.merchant_serial="
    7097             :                             "     (SELECT merchant_serial "
    7098             :                             "        FROM merchant_instances"
    7099             :                             "        WHERE merchant_id=$1)"
    7100             :                             "   AND"
    7101             :                             "    order_serial > $3"
    7102             :                             "   AND"
    7103             :                             "    creation_time > $4"
    7104             :                             "   AND"
    7105             :                             "    BOOL($5) = paid"
    7106             :                             "   AND"
    7107             :                             "    BOOL($6) = (order_serial IN"
    7108             :                             "     (SELECT order_serial "
    7109             :                             "      FROM merchant_refunds))"
    7110             :                             " ORDER BY order_serial ASC"
    7111             :                             " LIMIT $2)"
    7112             :                             " ORDER BY order_serial ASC"
    7113             :                             " LIMIT $2",
    7114             :                             7),
    7115          17 :     GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_wired",
    7116             :                             "(SELECT"
    7117             :                             " order_id"
    7118             :                             ",order_serial"
    7119             :                             ",creation_time"
    7120             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7121             :                             " FROM merchant_orders"
    7122             :                             " WHERE merchant_orders.merchant_serial="
    7123             :                             "     (SELECT merchant_serial "
    7124             :                             "        FROM merchant_instances"
    7125             :                             "        WHERE merchant_id=$1)"
    7126             :                             "   AND"
    7127             :                             "    order_serial > $3"
    7128             :                             "   AND"
    7129             :                             "    creation_time > $4"
    7130             :                             "   AND"
    7131             :                             "    NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
    7132             :                             "   AND"
    7133             :                             "    NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
    7134             :                             "   AND"
    7135             :                             "    order_serial NOT IN"
    7136             :                             "     (SELECT order_serial"
    7137             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7138             :                             " ORDER BY order_serial ASC"
    7139             :                             " LIMIT $2)"
    7140             :                             "UNION " /* union ensures elements are distinct! */
    7141             :                             "(SELECT"
    7142             :                             " order_id"
    7143             :                             ",order_serial"
    7144             :                             ",creation_time"
    7145             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7146             :                             " FROM merchant_contract_terms"
    7147             :                             " WHERE merchant_contract_terms.merchant_serial="
    7148             :                             "     (SELECT merchant_serial "
    7149             :                             "        FROM merchant_instances"
    7150             :                             "        WHERE merchant_id=$1)"
    7151             :                             "   AND"
    7152             :                             "    order_serial > $3"
    7153             :                             "   AND"
    7154             :                             "    creation_time > $4"
    7155             :                             "   AND"
    7156             :                             "    BOOL($5) = paid"
    7157             :                             "   AND"
    7158             :                             "    BOOL($7) = wired"
    7159             :                             " ORDER BY order_serial ASC"
    7160             :                             " LIMIT $2)"
    7161             :                             " ORDER BY order_serial ASC"
    7162             :                             " LIMIT $2",
    7163             :                             7),
    7164          17 :     GNUNET_PQ_make_prepare ("lookup_orders_inc_refunded_wired",
    7165             :                             "(SELECT"
    7166             :                             " order_id"
    7167             :                             ",order_serial"
    7168             :                             ",creation_time"
    7169             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7170             :                             " FROM merchant_orders"
    7171             :                             " WHERE merchant_orders.merchant_serial="
    7172             :                             "     (SELECT merchant_serial "
    7173             :                             "        FROM merchant_instances"
    7174             :                             "        WHERE merchant_id=$1)"
    7175             :                             "   AND"
    7176             :                             "    order_serial > $3"
    7177             :                             "   AND"
    7178             :                             "    creation_time > $4"
    7179             :                             "   AND"
    7180             :                             "    NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
    7181             :                             "   AND"
    7182             :                             "    NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
    7183             :                             "   AND"
    7184             :                             "    order_serial NOT IN"
    7185             :                             "     (SELECT order_serial"
    7186             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7187             :                             " ORDER BY order_serial ASC"
    7188             :                             " LIMIT $2)"
    7189             :                             "UNION " /* union ensures elements are distinct! */
    7190             :                             "(SELECT"
    7191             :                             " order_id"
    7192             :                             ",order_serial"
    7193             :                             ",creation_time"
    7194             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7195             :                             " FROM merchant_contract_terms"
    7196             :                             " WHERE merchant_contract_terms.merchant_serial="
    7197             :                             "     (SELECT merchant_serial "
    7198             :                             "        FROM merchant_instances"
    7199             :                             "        WHERE merchant_id=$1)"
    7200             :                             "   AND"
    7201             :                             "    order_serial > $3"
    7202             :                             "   AND"
    7203             :                             "    creation_time > $4"
    7204             :                             "   AND"
    7205             :                             "    BOOL($6) = (order_serial IN"
    7206             :                             "     (SELECT order_serial "
    7207             :                             "      FROM merchant_refunds))"
    7208             :                             "   AND"
    7209             :                             "    BOOL($7) = wired"
    7210             :                             " ORDER BY order_serial ASC"
    7211             :                             " LIMIT $2)"
    7212             :                             " ORDER BY order_serial ASC"
    7213             :                             " LIMIT $2",
    7214             :                             7),
    7215          17 :     GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_refunded_wired",
    7216             :                             "(SELECT"
    7217             :                             " order_id"
    7218             :                             ",order_serial"
    7219             :                             ",creation_time"
    7220             :                             " FROM merchant_orders"
    7221             :                             " WHERE merchant_orders.merchant_serial="
    7222             :                             "     (SELECT merchant_serial "
    7223             :                             "        FROM merchant_instances"
    7224             :                             "        WHERE merchant_id=$1)"
    7225             :                             "   AND"
    7226             :                             "    order_serial > $3"
    7227             :                             "   AND"
    7228             :                             "    creation_time > $4"
    7229             :                             "   AND"
    7230             :                             "    NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
    7231             :                             "   AND"
    7232             :                             "    NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
    7233             :                             "   AND"
    7234             :                             "    NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
    7235             :                             "   AND"
    7236             :                             "    order_serial NOT IN"
    7237             :                             "     (SELECT order_serial"
    7238             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7239             :                             " ORDER BY order_serial ASC"
    7240             :                             " LIMIT $2)"
    7241             :                             "UNION " /* union ensures elements are distinct! */
    7242             :                             "(SELECT"
    7243             :                             " order_id"
    7244             :                             ",order_serial"
    7245             :                             ",creation_time"
    7246             :                             " FROM merchant_contract_terms"
    7247             :                             " WHERE merchant_contract_terms.merchant_serial="
    7248             :                             "     (SELECT merchant_serial "
    7249             :                             "        FROM merchant_instances"
    7250             :                             "        WHERE merchant_id=$1)"
    7251             :                             "   AND"
    7252             :                             "    order_serial > $3"
    7253             :                             "   AND"
    7254             :                             "    creation_time > $4"
    7255             :                             "   AND"
    7256             :                             "    BOOL($5) = paid"
    7257             :                             "   AND"
    7258             :                             "    BOOL($6) = (order_serial IN"
    7259             :                             "     (SELECT order_serial "
    7260             :                             "      FROM merchant_refunds))"
    7261             :                             "   AND"
    7262             :                             "    BOOL($7) = wired"
    7263             :                             " ORDER BY order_serial ASC"
    7264             :                             " LIMIT $2)"
    7265             :                             " ORDER BY order_serial ASC"
    7266             :                             " LIMIT $2",
    7267             :                             7),
    7268          17 :     GNUNET_PQ_make_prepare ("lookup_orders_dec",
    7269             :                             "(SELECT"
    7270             :                             " order_id"
    7271             :                             ",order_serial"
    7272             :                             ",creation_time"
    7273             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7274             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7275             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7276             :                             " FROM merchant_orders"
    7277             :                             " WHERE merchant_orders.merchant_serial="
    7278             :                             "     (SELECT merchant_serial "
    7279             :                             "        FROM merchant_instances"
    7280             :                             "        WHERE merchant_id=$1)"
    7281             :                             "   AND"
    7282             :                             "    order_serial < $3"
    7283             :                             "   AND"
    7284             :                             "    creation_time < $4"
    7285             :                             " ORDER BY order_serial DESC"
    7286             :                             " LIMIT $2)"
    7287             :                             "UNION " /* union ensures elements are distinct! */
    7288             :                             "(SELECT"
    7289             :                             " order_id"
    7290             :                             ",order_serial"
    7291             :                             ",creation_time"
    7292             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7293             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7294             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7295             :                             " FROM merchant_contract_terms"
    7296             :                             " WHERE merchant_contract_terms.merchant_serial="
    7297             :                             "     (SELECT merchant_serial "
    7298             :                             "        FROM merchant_instances"
    7299             :                             "        WHERE merchant_id=$1)"
    7300             :                             "   AND"
    7301             :                             "    order_serial < $3"
    7302             :                             "   AND"
    7303             :                             "    creation_time < $4"
    7304             :                             " ORDER BY order_serial DESC"
    7305             :                             " LIMIT $2)"
    7306             :                             " ORDER BY order_serial DESC"
    7307             :                             " LIMIT $2",
    7308             :                             7),
    7309          17 :     GNUNET_PQ_make_prepare ("lookup_orders_dec_paid",
    7310             :                             "(SELECT"
    7311             :                             " order_id"
    7312             :                             ",order_serial"
    7313             :                             ",creation_time"
    7314             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7315             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7316             :                             " FROM merchant_orders"
    7317             :                             " WHERE merchant_orders.merchant_serial="
    7318             :                             "     (SELECT merchant_serial "
    7319             :                             "        FROM merchant_instances"
    7320             :                             "        WHERE merchant_id=$1)"
    7321             :                             "   AND"
    7322             :                             "    order_serial < $3"
    7323             :                             "   AND"
    7324             :                             "    creation_time < $4"
    7325             :                             "   AND"
    7326             :                             "    NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
    7327             :                             "   AND"
    7328             :                             "    order_serial NOT IN"
    7329             :                             "     (SELECT order_serial"
    7330             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7331             :                             " ORDER BY order_serial DESC"
    7332             :                             " LIMIT $2)"
    7333             :                             "UNION " /* union ensures elements are distinct! */
    7334             :                             "(SELECT"
    7335             :                             " order_id"
    7336             :                             ",order_serial"
    7337             :                             ",creation_time"
    7338             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7339             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7340             :                             " FROM merchant_contract_terms"
    7341             :                             " WHERE merchant_contract_terms.merchant_serial="
    7342             :                             "     (SELECT merchant_serial "
    7343             :                             "        FROM merchant_instances"
    7344             :                             "        WHERE merchant_id=$1)"
    7345             :                             "   AND"
    7346             :                             "    order_serial < $3"
    7347             :                             "   AND"
    7348             :                             "    creation_time < $4"
    7349             :                             "   AND"
    7350             :                             "    BOOL($5) = paid"
    7351             :                             " ORDER BY order_serial DESC"
    7352             :                             " LIMIT $2)"
    7353             :                             " ORDER BY order_serial DESC"
    7354             :                             " LIMIT $2",
    7355             :                             7),
    7356          17 :     GNUNET_PQ_make_prepare ("lookup_orders_dec_refunded",
    7357             :                             "(SELECT"
    7358             :                             " order_id"
    7359             :                             ",order_serial"
    7360             :                             ",creation_time"
    7361             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7362             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7363             :                             " FROM merchant_orders"
    7364             :                             " WHERE merchant_orders.merchant_serial="
    7365             :                             "     (SELECT merchant_serial "
    7366             :                             "        FROM merchant_instances"
    7367             :                             "        WHERE merchant_id=$1)"
    7368             :                             "   AND"
    7369             :                             "    order_serial < $3"
    7370             :                             "   AND"
    7371             :                             "    creation_time < $4"
    7372             :                             "   AND"
    7373             :                             "    NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
    7374             :                             "   AND"
    7375             :                             "    order_serial NOT IN"
    7376             :                             "     (SELECT order_serial"
    7377             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7378             :                             " ORDER BY order_serial DESC"
    7379             :                             " LIMIT $2)"
    7380             :                             "UNION " /* union ensures elements are distinct! */
    7381             :                             "(SELECT"
    7382             :                             " order_id"
    7383             :                             ",order_serial"
    7384             :                             ",creation_time"
    7385             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7386             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7387             :                             " FROM merchant_contract_terms"
    7388             :                             " WHERE merchant_contract_terms.merchant_serial="
    7389             :                             "     (SELECT merchant_serial "
    7390             :                             "        FROM merchant_instances"
    7391             :                             "        WHERE merchant_id=$1)"
    7392             :                             "   AND"
    7393             :                             "    order_serial < $3"
    7394             :                             "   AND"
    7395             :                             "    creation_time < $4"
    7396             :                             "   AND"
    7397             :                             "    BOOL($6) = (order_serial IN"
    7398             :                             "     (SELECT order_serial "
    7399             :                             "      FROM merchant_refunds))"
    7400             :                             " ORDER BY order_serial DESC"
    7401             :                             " LIMIT $2)"
    7402             :                             " ORDER BY order_serial DESC"
    7403             :                             " LIMIT $2",
    7404             :                             7),
    7405          17 :     GNUNET_PQ_make_prepare ("lookup_orders_dec_wired",
    7406             :                             "(SELECT"
    7407             :                             " order_id"
    7408             :                             ",order_serial"
    7409             :                             ",creation_time"
    7410             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7411             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7412             :                             " FROM merchant_orders"
    7413             :                             " WHERE merchant_orders.merchant_serial="
    7414             :                             "     (SELECT merchant_serial "
    7415             :                             "        FROM merchant_instances"
    7416             :                             "        WHERE merchant_id=$1)"
    7417             :                             "   AND"
    7418             :                             "    order_serial < $3"
    7419             :                             "   AND"
    7420             :                             "    creation_time < $4"
    7421             :                             "   AND"
    7422             :                             "    NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
    7423             :                             "   AND"
    7424             :                             "    order_serial NOT IN"
    7425             :                             "     (SELECT order_serial"
    7426             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7427             :                             " ORDER BY order_serial DESC"
    7428             :                             " LIMIT $2)"
    7429             :                             "UNION " /* union ensures elements are distinct! */
    7430             :                             "(SELECT"
    7431             :                             " order_id"
    7432             :                             ",order_serial"
    7433             :                             ",creation_time"
    7434             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7435             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7436             :                             " FROM merchant_contract_terms"
    7437             :                             " WHERE merchant_contract_terms.merchant_serial="
    7438             :                             "     (SELECT merchant_serial "
    7439             :                             "        FROM merchant_instances"
    7440             :                             "        WHERE merchant_id=$1)"
    7441             :                             "   AND"
    7442             :                             "    order_serial < $3"
    7443             :                             "   AND"
    7444             :                             "    creation_time < $4"
    7445             :                             "   AND"
    7446             :                             "    BOOL($7) = wired"
    7447             :                             " ORDER BY order_serial DESC"
    7448             :                             " LIMIT $2)"
    7449             :                             " ORDER BY order_serial DESC"
    7450             :                             " LIMIT $2",
    7451             :                             7),
    7452          17 :     GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_refunded",
    7453             :                             "(SELECT"
    7454             :                             " order_id"
    7455             :                             ",order_serial"
    7456             :                             ",creation_time"
    7457             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7458             :                             " FROM merchant_orders"
    7459             :                             " WHERE merchant_orders.merchant_serial="
    7460             :                             "     (SELECT merchant_serial "
    7461             :                             "        FROM merchant_instances"
    7462             :                             "        WHERE merchant_id=$1)"
    7463             :                             "   AND"
    7464             :                             "    order_serial < $3"
    7465             :                             "   AND"
    7466             :                             "    creation_time < $4"
    7467             :                             "   AND"
    7468             :                             "    NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
    7469             :                             "   AND"
    7470             :                             "    NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
    7471             :                             "   AND"
    7472             :                             "    order_serial NOT IN"
    7473             :                             "     (SELECT order_serial"
    7474             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7475             :                             " ORDER BY order_serial DESC"
    7476             :                             " LIMIT $2)"
    7477             :                             "UNION " /* union ensures elements are distinct! */
    7478             :                             "(SELECT"
    7479             :                             " order_id"
    7480             :                             ",order_serial"
    7481             :                             ",creation_time"
    7482             :                             ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
    7483             :                             " FROM merchant_contract_terms"
    7484             :                             " WHERE merchant_contract_terms.merchant_serial="
    7485             :                             "     (SELECT merchant_serial "
    7486             :                             "        FROM merchant_instances"
    7487             :                             "        WHERE merchant_id=$1)"
    7488             :                             "   AND"
    7489             :                             "    order_serial < $3"
    7490             :                             "   AND"
    7491             :                             "    creation_time < $4"
    7492             :                             "   AND"
    7493             :                             "    BOOL($5) = paid"
    7494             :                             "   AND"
    7495             :                             "    BOOL($6) = (order_serial IN"
    7496             :                             "     (SELECT order_serial "
    7497             :                             "      FROM merchant_refunds))"
    7498             :                             " ORDER BY order_serial DESC"
    7499             :                             " LIMIT $2)"
    7500             :                             " ORDER BY order_serial DESC"
    7501             :                             " LIMIT $2",
    7502             :                             7),
    7503          17 :     GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_wired",
    7504             :                             "(SELECT"
    7505             :                             " order_id"
    7506             :                             ",order_serial"
    7507             :                             ",creation_time"
    7508             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7509             :                             " FROM merchant_orders"
    7510             :                             " WHERE merchant_orders.merchant_serial="
    7511             :                             "     (SELECT merchant_serial "
    7512             :                             "        FROM merchant_instances"
    7513             :                             "        WHERE merchant_id=$1)"
    7514             :                             "   AND"
    7515             :                             "    order_serial < $3"
    7516             :                             "   AND"
    7517             :                             "    creation_time < $4"
    7518             :                             "   AND"
    7519             :                             "    NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
    7520             :                             "   AND"
    7521             :                             "    NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
    7522             :                             "   AND"
    7523             :                             "    order_serial NOT IN"
    7524             :                             "     (SELECT order_serial"
    7525             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7526             :                             " ORDER BY order_serial DESC"
    7527             :                             " LIMIT $2)"
    7528             :                             "UNION " /* union ensures elements are distinct! */
    7529             :                             "(SELECT"
    7530             :                             " order_id"
    7531             :                             ",order_serial"
    7532             :                             ",creation_time"
    7533             :                             ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
    7534             :                             " FROM merchant_contract_terms"
    7535             :                             " WHERE merchant_contract_terms.merchant_serial="
    7536             :                             "     (SELECT merchant_serial "
    7537             :                             "        FROM merchant_instances"
    7538             :                             "        WHERE merchant_id=$1)"
    7539             :                             "   AND"
    7540             :                             "    order_serial < $3"
    7541             :                             "   AND"
    7542             :                             "    creation_time < $4"
    7543             :                             "   AND"
    7544             :                             "    BOOL($5) = paid"
    7545             :                             "   AND"
    7546             :                             "    BOOL($7) = wired"
    7547             :                             " ORDER BY order_serial DESC"
    7548             :                             " LIMIT $2)"
    7549             :                             " ORDER BY order_serial DESC"
    7550             :                             " LIMIT $2",
    7551             :                             7),
    7552          17 :     GNUNET_PQ_make_prepare ("lookup_orders_dec_refunded_wired",
    7553             :                             "(SELECT"
    7554             :                             " order_id"
    7555             :                             ",order_serial"
    7556             :                             ",creation_time"
    7557             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7558             :                             " FROM merchant_orders"
    7559             :                             " WHERE merchant_orders.merchant_serial="
    7560             :                             "     (SELECT merchant_serial "
    7561             :                             "        FROM merchant_instances"
    7562             :                             "        WHERE merchant_id=$1)"
    7563             :                             "   AND"
    7564             :                             "    order_serial < $3"
    7565             :                             "   AND"
    7566             :                             "    creation_time < $4"
    7567             :                             "   AND"
    7568             :                             "    NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
    7569             :                             "   AND"
    7570             :                             "    NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
    7571             :                             "   AND"
    7572             :                             "    order_serial NOT IN"
    7573             :                             "     (SELECT order_serial"
    7574             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7575             :                             " ORDER BY order_serial DESC"
    7576             :                             " LIMIT $2)"
    7577             :                             "UNION " /* union ensures elements are distinct! */
    7578             :                             "(SELECT"
    7579             :                             " order_id"
    7580             :                             ",order_serial"
    7581             :                             ",creation_time"
    7582             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    7583             :                             " FROM merchant_contract_terms"
    7584             :                             " WHERE merchant_contract_terms.merchant_serial="
    7585             :                             "     (SELECT merchant_serial "
    7586             :                             "        FROM merchant_instances"
    7587             :                             "        WHERE merchant_id=$1)"
    7588             :                             "   AND"
    7589             :                             "    order_serial < $3"
    7590             :                             "   AND"
    7591             :                             "    creation_time < $4"
    7592             :                             "   AND"
    7593             :                             "    BOOL($6) = (order_serial IN"
    7594             :                             "     (SELECT order_serial "
    7595             :                             "      FROM merchant_refunds))"
    7596             :                             "   AND"
    7597             :                             "    BOOL($7) = wired"
    7598             :                             " ORDER BY order_serial DESC"
    7599             :                             " LIMIT $2)"
    7600             :                             " ORDER BY order_serial DESC"
    7601             :                             " LIMIT $2",
    7602             :                             7),
    7603          17 :     GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_refunded_wired",
    7604             :                             "(SELECT"
    7605             :                             " order_id"
    7606             :                             ",order_serial"
    7607             :                             ",creation_time"
    7608             :                             " FROM merchant_orders"
    7609             :                             " WHERE merchant_orders.merchant_serial="
    7610             :                             "     (SELECT merchant_serial "
    7611             :                             "        FROM merchant_instances"
    7612             :                             "        WHERE merchant_id=$1)"
    7613             :                             "   AND"
    7614             :                             "    order_serial < $3"
    7615             :                             "   AND"
    7616             :                             "    creation_time < $4"
    7617             :                             "   AND"
    7618             :                             "    NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
    7619             :                             "   AND"
    7620             :                             "    NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
    7621             :                             "   AND"
    7622             :                             "    NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
    7623             :                             "   AND"
    7624             :                             "    order_serial NOT IN"
    7625             :                             "     (SELECT order_serial"
    7626             :                             "      FROM merchant_contract_terms)" /* only select unclaimed orders */
    7627             :                             " ORDER BY order_serial DESC"
    7628             :                             " LIMIT $2)"
    7629             :                             "UNION " /* union ensures elements are distinct! */
    7630             :                             "(SELECT"
    7631             :                             " order_id"
    7632             :                             ",order_serial"
    7633             :                             ",creation_time"
    7634             :                             " FROM merchant_contract_terms"
    7635             :                             " WHERE merchant_contract_terms.merchant_serial="
    7636             :                             "     (SELECT merchant_serial "
    7637             :                             "        FROM merchant_instances"
    7638             :                             "        WHERE merchant_id=$1)"
    7639             :                             "   AND"
    7640             :                             "    order_serial < $3"
    7641             :                             "   AND"
    7642             :                             "    creation_time < $4"
    7643             :                             "   AND"
    7644             :                             "    BOOL($5) = paid"
    7645             :                             "   AND"
    7646             :                             "    BOOL($6) = (order_serial IN"
    7647             :                             "     (SELECT order_serial "
    7648             :                             "      FROM merchant_refunds))"
    7649             :                             "   AND"
    7650             :                             "    BOOL($7) = wired"
    7651             :                             " ORDER BY order_serial DESC"
    7652             :                             " LIMIT $2)"
    7653             :                             " ORDER BY order_serial DESC"
    7654             :                             " LIMIT $2",
    7655             :                             7),
    7656             :     /* for postgres_insert_order() */
    7657          17 :     GNUNET_PQ_make_prepare ("insert_order",
    7658             :                             "INSERT INTO merchant_orders"
    7659             :                             "(merchant_serial"
    7660             :                             ",order_id"
    7661             :                             ",pay_deadline"
    7662             :                             ",claim_token"
    7663             :                             ",h_post_data"
    7664             :                             ",creation_time"
    7665             :                             ",contract_terms)"
    7666             :                             " SELECT merchant_serial,"
    7667             :                             " $2, $3, $4, $5, $6, $7"
    7668             :                             " FROM merchant_instances"
    7669             :                             " WHERE merchant_id=$1",
    7670             :                             7),
    7671             :     /* for postgres_unlock_inventory() */
    7672          17 :     GNUNET_PQ_make_prepare ("unlock_inventory",
    7673             :                             "DELETE"
    7674             :                             " FROM merchant_inventory_locks"
    7675             :                             " WHERE lock_uuid=$1",
    7676             :                             1),
    7677             :     /* for postgres_insert_order_lock() */
    7678          17 :     GNUNET_PQ_make_prepare ("insert_order_lock",
    7679             :                             "WITH tmp AS"
    7680             :                             "  (SELECT "
    7681             :                             "      product_serial"
    7682             :                             "     ,merchant_serial"
    7683             :                             "     ,total_stock"
    7684             :                             "     ,total_sold"
    7685             :                             "     ,total_lost"
    7686             :                             "   FROM merchant_inventory"
    7687             :                             "   WHERE product_id=$3"
    7688             :                             "     AND merchant_serial="
    7689             :                             "     (SELECT merchant_serial"
    7690             :                             "        FROM merchant_instances"
    7691             :                             "        WHERE merchant_id=$1))"
    7692             :                             " INSERT INTO merchant_order_locks"
    7693             :                             " (product_serial"
    7694             :                             " ,total_locked"
    7695             :                             " ,order_serial)"
    7696             :                             " SELECT tmp.product_serial, $4, order_serial"
    7697             :                             "   FROM merchant_orders"
    7698             :                             "   JOIN tmp USING(merchant_serial)"
    7699             :                             "   WHERE order_id=$2 AND"
    7700             :                             "     tmp.total_stock - tmp.total_sold - tmp.total_lost - $4 >= "
    7701             :                             "     (SELECT COALESCE(SUM(total_locked), 0)"
    7702             :                             "        FROM merchant_inventory_locks"
    7703             :                             "        WHERE product_serial=tmp.product_serial) + "
    7704             :                             "     (SELECT COALESCE(SUM(total_locked), 0)"
    7705             :                             "        FROM merchant_order_locks"
    7706             :                             "        WHERE product_serial=tmp.product_serial)",
    7707             :                             4),
    7708             :     /* for postgres_lookup_contract_terms() */
    7709          17 :     GNUNET_PQ_make_prepare ("lookup_contract_terms",
    7710             :                             "SELECT"
    7711             :                             " contract_terms"
    7712             :                             ",order_serial"
    7713             :                             ",claim_token"
    7714             :                             " FROM merchant_contract_terms"
    7715             :                             " WHERE order_id=$2"
    7716             :                             "   AND merchant_serial="
    7717             :                             "     (SELECT merchant_serial"
    7718             :                             "        FROM merchant_instances"
    7719             :                             "        WHERE merchant_id=$1)",
    7720             :                             2),
    7721             :     /* for postgres_insert_contract_terms() */
    7722          17 :     GNUNET_PQ_make_prepare ("insert_contract_terms",
    7723             :                             "INSERT INTO merchant_contract_terms"
    7724             :                             "(order_serial"
    7725             :                             ",merchant_serial"
    7726             :                             ",order_id"
    7727             :                             ",contract_terms"
    7728             :                             ",h_contract_terms"
    7729             :                             ",creation_time"
    7730             :                             ",pay_deadline"
    7731             :                             ",refund_deadline"
    7732             :                             ",fulfillment_url"
    7733             :                             ",claim_token)"
    7734             :                             "SELECT"
    7735             :                             " mo.order_serial"
    7736             :                             ",mo.merchant_serial"
    7737             :                             ",mo.order_id"
    7738             :                             ",$3"  /* contract_terms */
    7739             :                             ",$4"  /* h_contract_terms */
    7740             :                             ",mo.creation_time"
    7741             :                             ",$5" /* pay_deadline */
    7742             :                             ",$6" /* refund_deadline */
    7743             :                             ",$7" /* fulfillment_url */
    7744             :                             ",mo.claim_token "
    7745             :                             "FROM merchant_orders mo"
    7746             :                             " WHERE order_id=$2"
    7747             :                             "   AND merchant_serial="
    7748             :                             "     (SELECT merchant_serial"
    7749             :                             "        FROM merchant_instances"
    7750             :                             "        WHERE merchant_id=$1)",
    7751             :                             7),
    7752             :     /* for postgres_update_contract_terms() */
    7753          17 :     GNUNET_PQ_make_prepare ("update_contract_terms",
    7754             :                             "UPDATE merchant_contract_terms SET"
    7755             :                             " contract_terms=$3"
    7756             :                             ",h_contract_terms=$4"
    7757             :                             ",pay_deadline=$5"
    7758             :                             ",refund_deadline=$6"
    7759             :                             ",fulfillment_url=$7"
    7760             :                             " WHERE order_id=$2"
    7761             :                             "   AND merchant_serial="
    7762             :                             "     (SELECT merchant_serial"
    7763             :                             "        FROM merchant_instances"
    7764             :                             "        WHERE merchant_id=$1)",
    7765             :                             7),
    7766             :     /* for postgres_delete_contract_terms() */
    7767          17 :     GNUNET_PQ_make_prepare ("delete_contract_terms",
    7768             :                             "DELETE FROM merchant_contract_terms"
    7769             :                             " WHERE order_id=$2"
    7770             :                             "   AND merchant_serial="
    7771             :                             "     (SELECT merchant_serial"
    7772             :                             "        FROM merchant_instances"
    7773             :                             "        WHERE merchant_id=$1)"
    7774             :                             "   AND ( ( (pay_deadline < $4) AND"
    7775             :                             "           (NOT paid) ) OR"
    7776             :                             "         (creation_time + $3 < $4) )",
    7777             :                             4),
    7778             :     /* for postgres_lookup_deposits() */
    7779          17 :     GNUNET_PQ_make_prepare ("lookup_deposits",
    7780             :                             "SELECT"
    7781             :                             " exchange_url"
    7782             :                             ",coin_pub"
    7783             :                             ",amount_with_fee_val"
    7784             :                             ",amount_with_fee_frac"
    7785             :                             ",deposit_fee_val"
    7786             :                             ",deposit_fee_frac"
    7787             :                             ",refund_fee_val"
    7788             :                             ",refund_fee_frac"
    7789             :                             ",wire_fee_val"
    7790             :                             ",wire_fee_frac"
    7791             :                             " FROM merchant_deposits"
    7792             :                             " WHERE order_serial="
    7793             :                             "     (SELECT order_serial"
    7794             :                             "        FROM merchant_contract_terms"
    7795             :                             "        WHERE h_contract_terms=$2"
    7796             :                             "          AND merchant_serial="
    7797             :                             "          (SELECT merchant_serial"
    7798             :                             "             FROM merchant_instances"
    7799             :                             "            WHERE merchant_id=$1))",
    7800             :                             2),
    7801             :     /* for postgres_insert_exchange_signkey() */
    7802          17 :     GNUNET_PQ_make_prepare ("insert_exchange_signkey",
    7803             :                             "INSERT INTO merchant_exchange_signing_keys"
    7804             :                             "(master_pub"
    7805             :                             ",exchange_pub"
    7806             :                             ",start_date"
    7807             :                             ",expire_date"
    7808             :                             ",end_date"
    7809             :                             ",master_sig)"
    7810             :                             "VALUES"
    7811             :                             "($1, $2, $3, $4, $5, $6)",
    7812             :                             6),
    7813             :     /* for postgres_insert_deposit() */
    7814          17 :     GNUNET_PQ_make_prepare ("insert_deposit",
    7815             :                             "WITH md AS"
    7816             :                             "  (SELECT account_serial, merchant_serial"
    7817             :                             "   FROM merchant_accounts"
    7818             :                             "   WHERE h_wire=$14"
    7819             :                             "    AND merchant_serial="
    7820             :                             "     (SELECT merchant_serial"
    7821             :                             "        FROM merchant_instances"
    7822             :                             "        WHERE merchant_id=$1))"
    7823             :                             ", ed AS"
    7824             :                             "  (SELECT signkey_serial"
    7825             :                             "   FROM merchant_exchange_signing_keys"
    7826             :                             "   WHERE exchange_pub=$16"
    7827             :                             "   ORDER BY start_date DESC"
    7828             :                             "   LIMIT 1)"
    7829             :                             "INSERT INTO merchant_deposits"
    7830             :                             "(order_serial"
    7831             :                             ",deposit_timestamp"
    7832             :                             ",coin_pub"
    7833             :                             ",exchange_url"
    7834             :                             ",amount_with_fee_val"
    7835             :                             ",amount_with_fee_frac"
    7836             :                             ",deposit_fee_val"
    7837             :                             ",deposit_fee_frac"
    7838             :                             ",refund_fee_val"
    7839             :                             ",refund_fee_frac"
    7840             :                             ",wire_fee_val"
    7841             :                             ",wire_fee_frac"
    7842             :                             ",exchange_sig"
    7843             :                             ",signkey_serial"
    7844             :                             ",account_serial)"
    7845             :                             " SELECT "
    7846             :                             "   order_serial"
    7847             :                             "  ,$3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $15"
    7848             :                             "  ,ed.signkey_serial"
    7849             :                             "  ,md.account_serial"
    7850             :                             "  FROM merchant_contract_terms"
    7851             :                             "   JOIN md USING (merchant_serial)"
    7852             :                             "   FULL OUTER JOIN ed ON TRUE"
    7853             :                             "  WHERE h_contract_terms=$2",
    7854             :                             16),
    7855             :     /* for postgres_lookup_refunds() */
    7856          17 :     GNUNET_PQ_make_prepare ("lookup_refunds",
    7857             :                             "SELECT"
    7858             :                             " coin_pub"
    7859             :                             ",refund_amount_val"
    7860             :                             ",refund_amount_frac"
    7861             :                             " FROM merchant_refunds"
    7862             :                             " WHERE order_serial="
    7863             :                             "  (SELECT order_serial"
    7864             :                             "     FROM merchant_contract_terms"
    7865             :                             "    WHERE h_contract_terms=$2"
    7866             :                             "      AND merchant_serial="
    7867             :                             "        (SELECT merchant_serial"
    7868             :                             "           FROM merchant_instances"
    7869             :                             "          WHERE merchant_id=$1))",
    7870             :                             2),
    7871             :     /* for postgres_mark_contract_paid() */
    7872          17 :     GNUNET_PQ_make_prepare ("mark_contract_paid",
    7873             :                             "UPDATE merchant_contract_terms SET"
    7874             :                             " paid=TRUE"
    7875             :                             ",session_id=$3"
    7876             :                             " WHERE h_contract_terms=$2"
    7877             :                             "   AND merchant_serial="
    7878             :                             "     (SELECT merchant_serial"
    7879             :                             "        FROM merchant_instances"
    7880             :                             "       WHERE merchant_id=$1)",
    7881             :                             3),
    7882             :     /* for postgres_mark_contract_paid() */
    7883          17 :     GNUNET_PQ_make_prepare ("mark_inventory_sold",
    7884             :                             "UPDATE merchant_inventory SET"
    7885             :                             " total_sold=total_sold + order_locks.total_locked"
    7886             :                             " FROM (SELECT total_locked,product_serial"
    7887             :                             "       FROM merchant_order_locks"
    7888             :                             "       WHERE order_serial="
    7889             :                             "       (SELECT order_serial"
    7890             :                             "          FROM merchant_contract_terms"
    7891             :                             "         WHERE h_contract_terms=$2"
    7892             :                             "           AND merchant_serial="
    7893             :                             "           (SELECT merchant_serial"
    7894             :                             "              FROM merchant_instances"
    7895             :                             "             WHERE merchant_id=$1))"
    7896             :                             "       ) AS order_locks"
    7897             :                             " WHERE merchant_inventory.product_serial"
    7898             :                             "             =order_locks.product_serial",
    7899             :                             2),
    7900             :     /* for postgres_mark_contract_paid() */
    7901          17 :     GNUNET_PQ_make_prepare ("delete_completed_order",
    7902             :                             "WITH md AS"
    7903             :                             "  (SELECT merchant_serial"
    7904             :                             "     FROM merchant_instances"
    7905             :                             "    WHERE merchant_id=$1) "
    7906             :                             "DELETE"
    7907             :                             " FROM merchant_orders"
    7908             :                             " WHERE order_serial="
    7909             :                             "       (SELECT order_serial"
    7910             :                             "          FROM merchant_contract_terms"
    7911             :                             "          JOIN md USING (merchant_serial)"
    7912             :                             "         WHERE h_contract_terms=$2)",
    7913             :                             2),
    7914             :     /* for postgres_refund_coin() */
    7915          17 :     GNUNET_PQ_make_prepare ("refund_coin",
    7916             :                             "INSERT INTO merchant_refunds"
    7917             :                             "(order_serial"
    7918             :                             ",rtransaction_id"
    7919             :                             ",refund_timestamp"
    7920             :                             ",coin_pub"
    7921             :                             ",reason"
    7922             :                             ",refund_amount_val"
    7923             :                             ",refund_amount_frac"
    7924             :                             ") "
    7925             :                             "SELECT "
    7926             :                             " order_serial"
    7927             :                             ",0" /* rtransaction_id always 0 for /abort */
    7928             :                             ",$3"
    7929             :                             ",coin_pub"
    7930             :                             ",$5"
    7931             :                             ",amount_with_fee_val"
    7932             :                             ",amount_with_fee_frac"
    7933             :                             " FROM merchant_deposits"
    7934             :                             " WHERE coin_pub=$4"
    7935             :                             "   AND order_serial="
    7936             :                             "  (SELECT order_serial"
    7937             :                             "     FROM merchant_contract_terms"
    7938             :                             "    WHERE h_contract_terms=$2"
    7939             :                             "      AND merchant_serial="
    7940             :                             "        (SELECT merchant_serial"
    7941             :                             "           FROM merchant_instances"
    7942             :                             "          WHERE merchant_id=$1))",
    7943             :                             5),
    7944             : 
    7945             :     /* for postgres_lookup_order_status() */
    7946          17 :     GNUNET_PQ_make_prepare ("lookup_order_status",
    7947             :                             "SELECT"
    7948             :                             " h_contract_terms"
    7949             :                             ",paid"
    7950             :                             " FROM merchant_contract_terms"
    7951             :                             " WHERE merchant_serial="
    7952             :                             "     (SELECT merchant_serial "
    7953             :                             "        FROM merchant_instances"
    7954             :                             "        WHERE merchant_id=$1)"
    7955             :                             "   AND order_id=$2",
    7956             :                             2),
    7957             : 
    7958             :     /* for postgres_lookup_order_status_by_serial() */
    7959          17 :     GNUNET_PQ_make_prepare ("lookup_order_status_by_serial",
    7960             :                             "SELECT"
    7961             :                             " h_contract_terms"
    7962             :                             ",order_id"
    7963             :                             ",paid"
    7964             :                             " FROM merchant_contract_terms"
    7965             :                             " WHERE merchant_serial="
    7966             :                             "     (SELECT merchant_serial "
    7967             :                             "        FROM merchant_instances"
    7968             :                             "        WHERE merchant_id=$1)"
    7969             :                             "   AND order_serial=$2",
    7970             :                             2),
    7971             : 
    7972             :     /* for postgres_lookup_payment_status() */
    7973          17 :     GNUNET_PQ_make_prepare ("lookup_payment_status",
    7974             :                             "SELECT"
    7975             :                             " wired"
    7976             :                             ",paid"
    7977             :                             " FROM merchant_contract_terms"
    7978             :                             " WHERE order_serial=$1",
    7979             :                             1),
    7980             :     /* for postgres_lookup_payment_status() */
    7981          17 :     GNUNET_PQ_make_prepare ("lookup_payment_status_session_id",
    7982             :                             "SELECT"
    7983             :                             " wired"
    7984             :                             ",paid"
    7985             :                             " FROM merchant_contract_terms"
    7986             :                             " WHERE order_serial=$1"
    7987             :                             "   AND session_id=$2",
    7988             :                             2),
    7989             :     /* for postgres_lookup_deposits_by_order() */
    7990          17 :     GNUNET_PQ_make_prepare ("lookup_deposits_by_order",
    7991             :                             "SELECT"
    7992             :                             " deposit_serial"
    7993             :                             ",exchange_url"
    7994             :                             ",h_wire"
    7995             :                             ",amount_with_fee_val"
    7996             :                             ",amount_with_fee_frac"
    7997             :                             ",deposit_fee_val"
    7998             :                             ",deposit_fee_frac"
    7999             :                             ",coin_pub"
    8000             :                             " FROM merchant_deposits"
    8001             :                             "  JOIN merchant_accounts USING (account_serial)"
    8002             :                             " WHERE order_serial=$1",
    8003             :                             1),
    8004             :     /* for postgres_lookup_transfer_details_by_order() */
    8005          17 :     GNUNET_PQ_make_prepare ("lookup_transfer_details_by_order",
    8006             :                             "SELECT"
    8007             :                             " md.deposit_serial"
    8008             :                             ",md.exchange_url"
    8009             :                             ",mt.wtid"
    8010             :                             ",exchange_deposit_value_val"
    8011             :                             ",exchange_deposit_value_frac"
    8012             :                             ",exchange_deposit_fee_val"
    8013             :                             ",exchange_deposit_fee_frac"
    8014             :                             ",deposit_timestamp"
    8015             :                             ",mt.confirmed AS transfer_confirmed"
    8016             :                             " FROM merchant_transfer_to_coin"
    8017             :                             " JOIN merchant_deposits AS md USING (deposit_serial)"
    8018             :                             " JOIN merchant_transfers AS mt USING (credit_serial)"
    8019             :                             " WHERE deposit_serial IN"
    8020             :                             "  (SELECT deposit_serial"
    8021             :                             "   FROM merchant_deposits"
    8022             :                             "   WHERE order_serial=$1)",
    8023             :                             1),
    8024             :     /* for postgres_insert_deposit_to_transfer() */
    8025          17 :     GNUNET_PQ_make_prepare ("insert_deposit_to_transfer",
    8026             :                             "INSERT INTO merchant_deposit_to_transfer"
    8027             :                             "(deposit_serial"
    8028             :                             ",coin_contribution_value_val"
    8029             :                             ",coin_contribution_value_frac"
    8030             :                             ",credit_serial"
    8031             :                             ",execution_time"
    8032             :                             ",signkey_serial"
    8033             :                             ",exchange_sig"
    8034             :                             ") SELECT $1, $2, $3, credit_serial, $4, signkey_serial, $5"
    8035             :                             " FROM merchant_transfers"
    8036             :                             " CROSS JOIN merchant_exchange_signing_keys"
    8037             :                             " WHERE exchange_pub=$6"
    8038             :                             "   AND wtid=$7",
    8039             :                             7),
    8040             :     /* for postgres_mark_order_wired() */
    8041          17 :     GNUNET_PQ_make_prepare ("mark_order_wired",
    8042             :                             "UPDATE merchant_contract_terms SET"
    8043             :                             " wired=true"
    8044             :                             " WHERE order_serial=$1",
    8045             :                             1),
    8046             :     /* for process_refund_cb() used in postgres_increase_refund() */
    8047          17 :     GNUNET_PQ_make_prepare ("find_refunds_by_coin",
    8048             :                             "SELECT"
    8049             :                             " refund_amount_val"
    8050             :                             ",refund_amount_frac"
    8051             :                             ",rtransaction_id"
    8052             :                             " FROM merchant_refunds"
    8053             :                             " WHERE coin_pub=$1"
    8054             :                             "   AND order_serial=$2",
    8055             :                             2),
    8056             :     /* for process_deposits_for_refund_cb() used in postgres_increase_refund() */
    8057          17 :     GNUNET_PQ_make_prepare ("insert_refund",
    8058             :                             "INSERT INTO merchant_refunds"
    8059             :                             "(order_serial"
    8060             :                             ",rtransaction_id"
    8061             :                             ",refund_timestamp"
    8062             :                             ",coin_pub"
    8063             :                             ",reason"
    8064             :                             ",refund_amount_val"
    8065             :                             ",refund_amount_frac"
    8066             :                             ") VALUES"
    8067             :                             "($1, $2, $3, $4, $5, $6, $7)",
    8068             :                             7),
    8069             :     /* for postgres_increase_refund() */
    8070          17 :     GNUNET_PQ_make_prepare ("find_deposits_for_refund",
    8071             :                             "SELECT"
    8072             :                             " coin_pub"
    8073             :                             ",order_serial"
    8074             :                             ",amount_with_fee_val"
    8075             :                             ",amount_with_fee_frac"
    8076             :                             " FROM merchant_deposits"
    8077             :                             " WHERE order_serial="
    8078             :                             "  (SELECT order_serial"
    8079             :                             "     FROM merchant_contract_terms"
    8080             :                             "    WHERE order_id=$2"
    8081             :                             "      AND paid=TRUE"
    8082             :                             "      AND merchant_serial="
    8083             :                             "        (SELECT merchant_serial"
    8084             :                             "           FROM merchant_instances"
    8085             :                             "          WHERE merchant_id=$1))",
    8086             :                             2),
    8087             :     /* for postgres_lookup_refunds_detailed() */
    8088          17 :     GNUNET_PQ_make_prepare ("lookup_refunds_detailed",
    8089             :                             "SELECT"
    8090             :                             " refund_serial"
    8091             :                             ",refund_timestamp"
    8092             :                             ",coin_pub"
    8093             :                             ",merchant_deposits.exchange_url"
    8094             :                             ",rtransaction_id"
    8095             :                             ",reason"
    8096             :                             ",refund_amount_val"
    8097             :                             ",refund_amount_frac"
    8098             :                             ",merchant_refund_proofs.exchange_sig IS NULL AS pending"
    8099             :                             " FROM merchant_refunds"
    8100             :                             "   JOIN merchant_deposits USING (order_serial, coin_pub)"
    8101             :                             "   LEFT JOIN merchant_refund_proofs USING (refund_serial)"
    8102             :                             " WHERE order_serial="
    8103             :                             "  (SELECT order_serial"
    8104             :                             "     FROM merchant_contract_terms"
    8105             :                             "    WHERE h_contract_terms=$2"
    8106             :                             "      AND merchant_serial="
    8107             :                             "        (SELECT merchant_serial"
    8108             :                             "           FROM merchant_instances"
    8109             :                             "          WHERE merchant_id=$1))",
    8110             :                             2),
    8111             :     /* for postgres_insert_refund_proof() */
    8112          17 :     GNUNET_PQ_make_prepare ("insert_refund_proof",
    8113             :                             "INSERT INTO merchant_refund_proofs"
    8114             :                             "(refund_serial"
    8115             :                             ",exchange_sig"
    8116             :                             ",signkey_serial)"
    8117             :                             "SELECT $1, $2, signkey_serial"
    8118             :                             " FROM merchant_exchange_signing_keys"
    8119             :                             " WHERE exchange_pub=$3"
    8120             :                             "  ORDER BY start_date DESC"
    8121             :                             "  LIMIT 1",
    8122             :                             5),
    8123             :     /* for postgres_lookup_refund_proof() */
    8124          17 :     GNUNET_PQ_make_prepare ("lookup_refund_proof",
    8125             :                             "SELECT"
    8126             :                             " merchant_exchange_signing_keys.exchange_pub"
    8127             :                             ",exchange_sig"
    8128             :                             " FROM merchant_refund_proofs"
    8129             :                             "  JOIN merchant_exchange_signing_keys"
    8130             :                             "    USING (signkey_serial)"
    8131             :                             " WHERE"
    8132             :                             "   refund_serial=$1",
    8133             :                             1),
    8134             :     /* for postgres_lookup_order_by_fulfillment() */
    8135          17 :     GNUNET_PQ_make_prepare ("lookup_order_by_fulfillment",
    8136             :                             "SELECT"
    8137             :                             " order_id"
    8138             :                             " FROM merchant_contract_terms"
    8139             :                             " WHERE fulfillment_url=$2"
    8140             :                             "   AND session_id=$3"
    8141             :                             "   AND merchant_serial="
    8142             :                             "        (SELECT merchant_serial"
    8143             :                             "           FROM merchant_instances"
    8144             :                             "          WHERE merchant_id=$1)",
    8145             :                             3),
    8146             :     /* for postgres_insert_transfer() */
    8147          17 :     GNUNET_PQ_make_prepare ("insert_transfer",
    8148             :                             "INSERT INTO merchant_transfers"
    8149             :                             "(exchange_url"
    8150             :                             ",wtid"
    8151             :                             ",credit_amount_val"
    8152             :                             ",credit_amount_frac"
    8153             :                             ",account_serial"
    8154             :                             ",confirmed)"
    8155             :                             "SELECT"
    8156             :                             " $1, $2, $3, $4, account_serial, $6"
    8157             :                             " FROM merchant_accounts"
    8158             :                             " WHERE payto_uri=$5"
    8159             :                             "   AND merchant_serial="
    8160             :                             "        (SELECT merchant_serial"
    8161             :                             "           FROM merchant_instances"
    8162             :                             "          WHERE merchant_id=$7)",
    8163             :                             7),
    8164             :     /* for postgres_delete_transfer() */
    8165          17 :     GNUNET_PQ_make_prepare ("delete_transfer",
    8166             :                             "DELETE FROM merchant_transfers"
    8167             :                             " WHERE"
    8168             :                             "   credit_serial=$2"
    8169             :                             "  AND account_serial IN "
    8170             :                             "  (SELECT account_serial "
    8171             :                             "     FROM merchant_accounts"
    8172             :                             "    WHERE merchant_serial="
    8173             :                             "     (SELECT merchant_serial"
    8174             :                             "        FROM merchant_instances"
    8175             :                             "       WHERE merchant_id=$1))",
    8176             :                             2),
    8177             :     /* for postgres_check_transfer_exists() */
    8178          17 :     GNUNET_PQ_make_prepare ("check_transfer_exists",
    8179             :                             "SELECT"
    8180             :                             " 1"
    8181             :                             " FROM merchant_transfers"
    8182             :                             " JOIN merchant_accounts"
    8183             :                             "  USING (account_serial)"
    8184             :                             " WHERE"
    8185             :                             "   credit_serial=$2"
    8186             :                             "  AND"
    8187             :                             "   merchant_serial="
    8188             :                             "     (SELECT merchant_serial"
    8189             :                             "        FROM merchant_instances"
    8190             :                             "       WHERE merchant_id=$1)",
    8191             :                             2),
    8192             :     /* for postgres_lookup_account() */
    8193          17 :     GNUNET_PQ_make_prepare ("lookup_account",
    8194             :                             "SELECT"
    8195             :                             " account_serial"
    8196             :                             " FROM merchant_accounts"
    8197             :                             " WHERE payto_uri=$2"
    8198             :                             "   AND merchant_serial="
    8199             :                             "        (SELECT merchant_serial"
    8200             :                             "           FROM merchant_instances"
    8201             :                             "          WHERE merchant_id=$1)",
    8202             :                             2),
    8203             :     /* for postgres_insert_transfer_details() */
    8204          17 :     GNUNET_PQ_make_prepare ("lookup_credit_serial",
    8205             :                             "SELECT"
    8206             :                             " credit_serial"
    8207             :                             " FROM merchant_transfers"
    8208             :                             " WHERE exchange_url=$1"
    8209             :                             "   AND wtid=$4"
    8210             :                             "   AND account_serial="
    8211             :                             "        (SELECT account_serial"
    8212             :                             "           FROM merchant_accounts"
    8213             :                             "          WHERE payto_uri=$2"
    8214             :                             "            AND exchange_url=$1"
    8215             :                             "            AND merchant_serial="
    8216             :                             "            (SELECT merchant_serial"
    8217             :                             "               FROM merchant_instances"
    8218             :                             "              WHERE merchant_id=$3))",
    8219             :                             4),
    8220             :     /* for postgres_insert_transfer_details() */
    8221          17 :     GNUNET_PQ_make_prepare ("insert_transfer_signature",
    8222             :                             "INSERT INTO merchant_transfer_signatures"
    8223             :                             "(credit_serial"
    8224             :                             ",signkey_serial"
    8225             :                             ",credit_amount_val"
    8226             :                             ",credit_amount_frac"
    8227             :                             ",wire_fee_val"
    8228             :                             ",wire_fee_frac"
    8229             :                             ",execution_time"
    8230             :                             ",exchange_sig) "
    8231             :                             "SELECT $1, signkey_serial, $2, $3, $4, $5, $6, $7"
    8232             :                             " FROM merchant_exchange_signing_keys"
    8233             :                             " WHERE exchange_pub=$8"
    8234             :                             "  ORDER BY start_date DESC"
    8235             :                             "  LIMIT 1",
    8236             :                             8),
    8237             :     /* for postgres_insert_transfer_details() */
    8238          17 :     GNUNET_PQ_make_prepare ("insert_transfer_to_coin_mapping",
    8239             :                             "INSERT INTO merchant_transfer_to_coin"
    8240             :                             "(deposit_serial"
    8241             :                             ",credit_serial"
    8242             :                             ",offset_in_exchange_list"
    8243             :                             ",exchange_deposit_value_val"
    8244             :                             ",exchange_deposit_value_frac"
    8245             :                             ",exchange_deposit_fee_val"
    8246             :                             ",exchange_deposit_fee_frac) "
    8247             :                             "SELECT deposit_serial, $1, $2, $3, $4, $5, $6"
    8248             :                             " FROM merchant_deposits"
    8249             :                             " JOIN merchant_contract_terms USING (order_serial)"
    8250             :                             " WHERE coin_pub=$7"
    8251             :                             "   AND h_contract_terms=$8",
    8252             :                             8),
    8253             :     /* for postgres_insert_transfer_details() */
    8254          17 :     GNUNET_PQ_make_prepare ("update_wired_by_coin_pub",
    8255             :                             "WITH os AS" /* select orders affected by the coin */
    8256             :                             "(SELECT order_serial"
    8257             :                             "   FROM merchant_deposits"
    8258             :                             "  WHERE coin_pub=$1)"
    8259             :                             "UPDATE merchant_contract_terms "
    8260             :                             " SET wired=TRUE "
    8261             :                             " WHERE order_serial IN "
    8262             :                             "  (SELECT order_serial FROM merchant_deposits" /* only orders for which NO un-wired coin exists*/
    8263             :                             "    WHERE NOT EXISTS "
    8264             :                             "    (SELECT order_serial FROM merchant_deposits" /* orders for which ANY un-wired coin exists */
    8265             :                             "       JOIN os USING (order_serial)" /* filter early */
    8266             :                             "      WHERE deposit_serial NOT IN"
    8267             :                             "      (SELECT deposit_serial " /* all coins associated with order that WERE wired */
    8268             :                             "         FROM merchant_deposits "
    8269             :                             "         JOIN os USING (order_serial)" /* filter early */
    8270             :                             "         JOIN merchant_deposit_to_transfer USING (deposit_serial)"
    8271             :                             "         JOIN merchant_transfers USING (credit_serial)"
    8272             :                             "        WHERE confirmed=TRUE)))",
    8273             :                             1),
    8274             :     /* for postgres_lookup_wire_fee() */
    8275          17 :     GNUNET_PQ_make_prepare ("lookup_wire_fee",
    8276             :                             "SELECT"
    8277             :                             " wire_fee_val"
    8278             :                             ",wire_fee_frac"
    8279             :                             ",closing_fee_val"
    8280             :                             ",closing_fee_frac"
    8281             :                             ",start_date"
    8282             :                             ",end_date"
    8283             :                             ",master_sig"
    8284             :                             " FROM merchant_exchange_wire_fees"
    8285             :                             " WHERE master_pub=$1"
    8286             :                             "   AND h_wire_method=$2"
    8287             :                             "   AND start_date <= $3"
    8288             :                             "   AND end_date > $3",
    8289             :                             3),
    8290             :     /* for postgres_lookup_deposits_by_contract_and_coin() */
    8291          17 :     GNUNET_PQ_make_prepare ("lookup_deposits_by_contract_and_coin",
    8292             :                             "SELECT"
    8293             :                             " exchange_url"
    8294             :                             ",amount_with_fee_val"
    8295             :                             ",amount_with_fee_frac"
    8296             :                             ",deposit_fee_val"
    8297             :                             ",deposit_fee_frac"
    8298             :                             ",refund_fee_val"
    8299             :                             ",refund_fee_frac"
    8300             :                             ",wire_fee_val"
    8301             :                             ",wire_fee_frac"
    8302             :                             ",h_wire"
    8303             :                             ",deposit_timestamp"
    8304             :                             ",refund_deadline"
    8305             :                             ",exchange_sig"
    8306             :                             ",exchange_pub"
    8307             :                             " FROM merchant_contract_terms"
    8308             :                             "  JOIN merchant_deposits USING (order_serial)"
    8309             :                             "  JOIN merchant_exchange_signing_keys USING (signkey_serial)"
    8310             :                             "  JOIN merchant_accounts USING (account_serial)"
    8311             :                             " WHERE h_contract_terms=$2"
    8312             :                             "   AND coin_pub=$3"
    8313             :                             "   AND merchant_contract_terms.merchant_serial="
    8314             :                             "     (SELECT merchant_serial"
    8315             :                             "        FROM merchant_instances"
    8316             :                             "       WHERE merchant_id=$1)",
    8317             :                             3),
    8318             :     /* for postgres_lookup_transfer() */
    8319          17 :     GNUNET_PQ_make_prepare ("lookup_transfer",
    8320             :                             "SELECT"
    8321             :                             " mt.credit_amount_val AS credit_amount_val"
    8322             :                             ",mt.credit_amount_frac AS credit_amount_frac"
    8323             :                             ",mts.credit_amount_val AS exchange_amount_val"
    8324             :                             ",mts.credit_amount_frac AS exchange_amount_frac"
    8325             :                             ",wire_fee_val"
    8326             :                             ",wire_fee_frac"
    8327             :                             ",execution_time"
    8328             :                             ",verified"
    8329             :                             " FROM merchant_transfers mt"
    8330             :                             "  JOIN merchant_accounts USING (account_serial)"
    8331             :                             "  JOIN merchant_instances USING (merchant_serial)"
    8332             :                             "  LEFT JOIN merchant_transfer_signatures mts USING (credit_serial)"
    8333             :                             " WHERE wtid=$2"
    8334             :                             "   AND exchange_url=$1"
    8335             :                             "   AND merchant_id=$3;",
    8336             :                             3),
    8337             :     /* for postgres_set_transfer_status_to_verified() */
    8338          17 :     GNUNET_PQ_make_prepare ("set_transfer_status_to_verified",
    8339             :                             "UPDATE merchant_transfers SET"
    8340             :                             " verified=TRUE"
    8341             :                             " WHERE wtid=$1"
    8342             :                             "   AND exchange_url=$2",
    8343             :                             2),
    8344             :     /* for postgres_lookup_transfer_summary() */
    8345          17 :     GNUNET_PQ_make_prepare ("lookup_transfer_summary",
    8346             :                             "SELECT"
    8347             :                             " order_id"
    8348             :                             ",exchange_deposit_value_val"
    8349             :                             ",exchange_deposit_value_frac"
    8350             :                             ",exchange_deposit_fee_val"
    8351             :                             ",exchange_deposit_fee_frac"
    8352             :                             " FROM merchant_transfers"
    8353             :                             "  JOIN merchant_transfer_to_coin USING (credit_serial)"
    8354             :                             "  JOIN merchant_deposits USING (deposit_serial)"
    8355             :                             "  JOIN merchant_contract_terms USING (order_serial)"
    8356             :                             " WHERE wtid=$2"
    8357             :                             "   AND merchant_transfers.exchange_url=$1",
    8358             :                             2),
    8359             :     /* for postgres_lookup_transfer_details() */
    8360          17 :     GNUNET_PQ_make_prepare ("lookup_transfer_details",
    8361             :                             "SELECT"
    8362             :                             " merchant_contract_terms.h_contract_terms"
    8363             :                             ",merchant_transfer_to_coin.offset_in_exchange_list"
    8364             :                             ",merchant_deposits.coin_pub"
    8365             :                             ",exchange_deposit_value_val"
    8366             :                             ",exchange_deposit_value_frac"
    8367             :                             ",exchange_deposit_fee_val"
    8368             :                             ",exchange_deposit_fee_frac"
    8369             :                             " FROM merchant_transfer_to_coin"
    8370             :                             "  JOIN merchant_deposits USING (deposit_serial)"
    8371             :                             "  JOIN merchant_contract_terms USING (order_serial)"
    8372             :                             "  JOIN merchant_transfers USING (credit_serial)"
    8373             :                             " WHERE merchant_transfers.wtid=$2"
    8374             :                             "   AND merchant_transfers.exchange_url=$1",
    8375             :                             2),
    8376             :     /* for postgres_lookup_transfers() */
    8377          17 :     GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_asc",
    8378             :                             "SELECT"
    8379             :                             " mt.credit_amount_val"
    8380             :                             ",mt.credit_amount_frac"
    8381             :                             ",wtid"
    8382             :                             ",merchant_accounts.payto_uri"
    8383             :                             ",exchange_url"
    8384             :                             ",credit_serial"
    8385             :                             ",merchant_transfer_signatures.execution_time"
    8386             :                             ",verified"
    8387             :                             ",confirmed"
    8388             :                             " FROM merchant_transfers mt"
    8389             :                             "  JOIN merchant_accounts USING (account_serial)"
    8390             :                             "  JOIN merchant_transfer_signatures USING (credit_serial)"
    8391             :                             " WHERE execution_time < $2"
    8392             :                             "   AND execution_time >= $3"
    8393             :                             "   AND credit_serial > $4"
    8394             :                             "   AND payto_uri = $6"
    8395             :                             "   AND merchant_serial ="
    8396             :                             "     (SELECT merchant_serial"
    8397             :                             "        FROM merchant_instances"
    8398             :                             "       WHERE merchant_id=$1)"
    8399             :                             " ORDER BY credit_serial ASC"
    8400             :                             " LIMIT $5",
    8401             :                             6),
    8402             :     /* for postgres_lookup_transfers() */
    8403          17 :     GNUNET_PQ_make_prepare ("lookup_transfers_time_asc",
    8404             :                             "SELECT"
    8405             :                             " mt.credit_amount_val"
    8406             :                             ",mt.credit_amount_frac"
    8407             :                             ",wtid"
    8408             :                             ",merchant_accounts.payto_uri"
    8409             :                             ",exchange_url"
    8410             :                             ",credit_serial"
    8411             :                             ",merchant_transfer_signatures.execution_time"
    8412             :                             ",verified"
    8413             :                             ",confirmed"
    8414             :                             " FROM merchant_transfers mt"
    8415             :                             "  JOIN merchant_accounts USING (account_serial)"
    8416             :                             "  JOIN merchant_transfer_signatures USING (credit_serial)"
    8417             :                             " WHERE execution_time < $2"
    8418             :                             "   AND execution_time >= $3"
    8419             :                             "   AND credit_serial > $4"
    8420             :                             "   AND merchant_serial ="
    8421             :                             "     (SELECT merchant_serial"
    8422             :                             "        FROM merchant_instances"
    8423             :                             "       WHERE merchant_id=$1)"
    8424             :                             " ORDER BY credit_serial ASC"
    8425             :                             " LIMIT $5",
    8426             :                             5),
    8427             :     /* for postgres_lookup_transfers() */
    8428          17 :     GNUNET_PQ_make_prepare ("lookup_transfers_payto_asc",
    8429             :                             "SELECT"
    8430             :                             " mt.credit_amount_val"
    8431             :                             ",mt.credit_amount_frac"
    8432             :                             ",wtid"
    8433             :                             ",merchant_accounts.payto_uri"
    8434             :                             ",exchange_url"
    8435             :                             ",credit_serial"
    8436             :                             ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
    8437             :                             "   THEN 9223372036854775807" /* largest BIGINT possible */
    8438             :                             "   ELSE merchant_transfer_signatures.execution_time"
    8439             :                             " END AS execution_time"
    8440             :                             ",verified"
    8441             :                             ",confirmed"
    8442             :                             " FROM merchant_transfers mt"
    8443             :                             "  JOIN merchant_accounts USING (account_serial)"
    8444             :                             "  LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
    8445             :                             " WHERE credit_serial > $2"
    8446             :                             "   AND payto_uri = $4"
    8447             :                             "   AND merchant_serial ="
    8448             :                             "     (SELECT merchant_serial"
    8449             :                             "        FROM merchant_instances"
    8450             :                             "       WHERE merchant_id=$1)"
    8451             :                             " ORDER BY credit_serial ASC"
    8452             :                             " LIMIT $3",
    8453             :                             4),
    8454             :     /* for postgres_lookup_transfers() */
    8455          17 :     GNUNET_PQ_make_prepare ("lookup_transfers_asc",
    8456             :                             "SELECT"
    8457             :                             " mt.credit_amount_val"
    8458             :                             ",mt.credit_amount_frac"
    8459             :                             ",wtid"
    8460             :                             ",merchant_accounts.payto_uri"
    8461             :                             ",exchange_url"
    8462             :                             ",credit_serial"
    8463             :                             ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
    8464             :                             "   THEN 9223372036854775807" /* largest BIGINT possible */
    8465             :                             "   ELSE merchant_transfer_signatures.execution_time"
    8466             :                             " END AS execution_time"
    8467             :                             ",verified"
    8468             :                             ",confirmed"
    8469             :                             " FROM merchant_transfers mt"
    8470             :                             "  JOIN merchant_accounts USING (account_serial)"
    8471             :                             "  LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
    8472             :                             " WHERE credit_serial > $2"
    8473             :                             "   AND merchant_serial ="
    8474             :                             "     (SELECT merchant_serial"
    8475             :                             "        FROM merchant_instances"
    8476             :                             "       WHERE merchant_id=$1)"
    8477             :                             " ORDER BY credit_serial ASC"
    8478             :                             " LIMIT $3",
    8479             :                             3),
    8480             :     /* for postgres_lookup_transfers() */
    8481          17 :     GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_desc",
    8482             :                             "SELECT"
    8483             :                             " mt.credit_amount_val"
    8484             :                             ",mt.credit_amount_frac"
    8485             :                             ",wtid"
    8486             :                             ",merchant_accounts.payto_uri"
    8487             :                             ",exchange_url"
    8488             :                             ",credit_serial"
    8489             :                             ",merchant_transfer_signatures.execution_time"
    8490             :                             ",verified"
    8491             :                             ",confirmed"
    8492             :                             " FROM merchant_transfers mt"
    8493             :                             "  JOIN merchant_accounts USING (account_serial)"
    8494             :                             "  JOIN merchant_transfer_signatures USING (credit_serial)"
    8495             :                             " WHERE execution_time < $2"
    8496             :                             "   AND execution_time >= $3"
    8497             :                             "   AND credit_serial < $4"
    8498             :                             "   AND payto_uri = $6"
    8499             :                             "   AND merchant_serial ="
    8500             :                             "     (SELECT merchant_serial"
    8501             :                             "        FROM merchant_instances"
    8502             :                             "       WHERE merchant_id=$1)"
    8503             :                             " ORDER BY credit_serial DESC"
    8504             :                             " LIMIT $5",
    8505             :                             6),
    8506             :     /* for postgres_lookup_transfers() */
    8507          17 :     GNUNET_PQ_make_prepare ("lookup_transfers_time_desc",
    8508             :                             "SELECT"
    8509             :                             " mt.credit_amount_val"
    8510             :                             ",mt.credit_amount_frac"
    8511             :                             ",wtid"
    8512             :                             ",merchant_accounts.payto_uri"
    8513             :                             ",exchange_url"
    8514             :                             ",credit_serial"
    8515             :                             ",merchant_transfer_signatures.execution_time"
    8516             :                             ",verified"
    8517             :                             ",confirmed"
    8518             :                             " FROM merchant_transfers mt"
    8519             :                             "  JOIN merchant_accounts USING (account_serial)"
    8520             :                             "  JOIN merchant_transfer_signatures USING (credit_serial)"
    8521             :                             " WHERE execution_time < $2"
    8522             :                             "   AND execution_time >= $3"
    8523             :                             "   AND credit_serial < $4"
    8524             :                             "   AND merchant_serial ="
    8525             :                             "     (SELECT merchant_serial"
    8526             :                             "        FROM merchant_instances"
    8527             :                             "       WHERE merchant_id=$1)"
    8528             :                             " ORDER BY credit_serial DESC"
    8529             :                             " LIMIT $5",
    8530             :                             5),
    8531             :     /* for postgres_lookup_transfers() */
    8532          17 :     GNUNET_PQ_make_prepare ("lookup_transfers_payto_desc",
    8533             :                             "SELECT"
    8534             :                             " mt.credit_amount_val"
    8535             :                             ",mt.credit_amount_frac"
    8536             :                             ",wtid"
    8537             :                             ",merchant_accounts.payto_uri"
    8538             :                             ",exchange_url"
    8539             :                             ",credit_serial"
    8540             :                             ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
    8541             :                             "   THEN 9223372036854775807" /* largest BIGINT possible */
    8542             :                             "   ELSE merchant_transfer_signatures.execution_time"
    8543             :                             " END AS execution_time"
    8544             :                             ",verified"
    8545             :                             ",confirmed"
    8546             :                             " FROM merchant_transfers mt"
    8547             :                             "  JOIN merchant_accounts USING (account_serial)"
    8548             :                             "  LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
    8549             :                             " WHERE credit_serial < $2"
    8550             :                             "   AND payto_uri = $4"
    8551             :                             "   AND merchant_serial ="
    8552             :                             "     (SELECT merchant_serial"
    8553             :                             "        FROM merchant_instances"
    8554             :                             "       WHERE merchant_id=$1)"
    8555             :                             " ORDER BY credit_serial DESC"
    8556             :                             " LIMIT $3",
    8557             :                             4),
    8558             :     /* for postgres_lookup_transfers() */
    8559          17 :     GNUNET_PQ_make_prepare ("lookup_transfers_desc",
    8560             :                             "SELECT"
    8561             :                             " mt.credit_amount_val"
    8562             :                             ",mt.credit_amount_frac"
    8563             :                             ",wtid"
    8564             :                             ",merchant_accounts.payto_uri"
    8565             :                             ",exchange_url"
    8566             :                             ",credit_serial"
    8567             :                             ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
    8568             :                             "   THEN 9223372036854775807" /* largest BIGINT possible */
    8569             :                             "   ELSE merchant_transfer_signatures.execution_time"
    8570             :                             " END AS execution_time"
    8571             :                             ",verified"
    8572             :                             ",confirmed"
    8573             :                             " FROM merchant_transfers mt"
    8574             :                             "  JOIN merchant_accounts USING (account_serial)"
    8575             :                             "  LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
    8576             :                             " WHERE credit_serial < $2"
    8577             :                             "   AND merchant_serial ="
    8578             :                             "     (SELECT merchant_serial"
    8579             :                             "        FROM merchant_instances"
    8580             :                             "       WHERE merchant_id=$1)"
    8581             :                             " ORDER BY credit_serial DESC"
    8582             :                             " LIMIT $3",
    8583             :                             3),
    8584             :     /* For postgres_store_wire_fee_by_exchange() */
    8585          17 :     GNUNET_PQ_make_prepare ("insert_wire_fee",
    8586             :                             "INSERT INTO merchant_exchange_wire_fees"
    8587             :                             "(master_pub"
    8588             :                             ",h_wire_method"
    8589             :                             ",wire_fee_val"
    8590             :                             ",wire_fee_frac"
    8591             :                             ",closing_fee_val"
    8592             :                             ",closing_fee_frac"
    8593             :                             ",start_date"
    8594             :                             ",end_date"
    8595             :                             ",master_sig)"
    8596             :                             " VALUES "
    8597             :                             "($1, $2, $3, $4, $5, $6, $7, $8, $9)",
    8598             :                             9),
    8599             :     /* For postgres_insert_reserve() */
    8600          17 :     GNUNET_PQ_make_prepare ("insert_reserve",
    8601             :                             "INSERT INTO merchant_tip_reserves"
    8602             :                             "(reserve_pub"
    8603             :                             ",merchant_serial"
    8604             :                             ",creation_time"
    8605             :                             ",expiration"
    8606             :                             ",merchant_initial_balance_val"
    8607             :                             ",merchant_initial_balance_frac"
    8608             :                             ")"
    8609             :                             "SELECT $2, merchant_serial, $3, $4, $5, $6"
    8610             :                             " FROM merchant_instances"
    8611             :                             " WHERE merchant_id=$1",
    8612             :                             6),
    8613             :     /* For postgres_activate_reserve() */
    8614          17 :     GNUNET_PQ_make_prepare ("activate_reserve",
    8615             :                             "UPDATE merchant_tip_reserves SET"
    8616             :                             " exchange_initial_balance_val=$3"
    8617             :                             ",exchange_initial_balance_frac=$4"
    8618             :                             " WHERE reserve_pub=$2"
    8619             :                             " AND merchant_serial="
    8620             :                             "   (SELECT merchant_serial"
    8621             :                             "      FROM merchant_instances"
    8622             :                             "     WHERE merchant_id=$1)",
    8623             :                             4),
    8624             :     /* For postgres_insert_reserve() */
    8625          17 :     GNUNET_PQ_make_prepare ("insert_reserve_key",
    8626             :                             "INSERT INTO merchant_tip_reserve_keys"
    8627             :                             "(reserve_serial"
    8628             :                             ",reserve_priv"
    8629             :                             ",exchange_url"
    8630             :                             ",payto_uri"
    8631             :                             ")"
    8632             :                             "SELECT reserve_serial, $3, $4, $5"
    8633             :                             " FROM merchant_tip_reserves"
    8634             :                             " WHERE reserve_pub=$2"
    8635             :                             "   AND merchant_serial="
    8636             :                             "     (SELECT merchant_serial"
    8637             :                             "        FROM merchant_instances"
    8638             :                             "       WHERE merchant_id=$1)",
    8639             :                             5),
    8640             :     /* For postgres_lookup_reserves() */
    8641          17 :     GNUNET_PQ_make_prepare ("lookup_reserves",
    8642             :                             "SELECT"
    8643             :                             " reserve_pub"
    8644             :                             ",creation_time"
    8645             :                             ",expiration"
    8646             :                             ",merchant_initial_balance_val"
    8647             :                             ",merchant_initial_balance_frac"
    8648             :                             ",exchange_initial_balance_val"
    8649             :                             ",exchange_initial_balance_frac"
    8650             :                             ",tips_committed_val"
    8651             :                             ",tips_committed_frac"
    8652             :                             ",tips_picked_up_val"
    8653             :                             ",tips_picked_up_frac"
    8654             :                             ",reserve_priv IS NOT NULL AS active"
    8655             :                             " FROM merchant_tip_reserves"
    8656             :                             " FULL OUTER JOIN merchant_tip_reserve_keys USING (reserve_serial)"
    8657             :                             " WHERE creation_time > $2"
    8658             :                             "   AND merchant_serial ="
    8659             :                             "     (SELECT merchant_serial"
    8660             :                             "        FROM merchant_instances"
    8661             :                             "       WHERE merchant_id=$1)",
    8662             :                             2),
    8663             :     /* For postgres_lookup_pending_reserves() */
    8664          17 :     GNUNET_PQ_make_prepare ("lookup_pending_reserves",
    8665             :                             "SELECT"
    8666             :                             " reserve_pub"
    8667             :                             ",merchant_id"
    8668             :                             ",exchange_url"
    8669             :                             ",merchant_initial_balance_val"
    8670             :                             ",merchant_initial_balance_frac"
    8671             :                             " FROM merchant_tip_reserves"
    8672             :                             " JOIN merchant_instances USING (merchant_serial)"
    8673             :                             " JOIN merchant_tip_reserve_keys USING (reserve_serial)"
    8674             :                             " WHERE exchange_initial_balance_val=0"
    8675             :                             "   AND exchange_initial_balance_frac=0",
    8676             :                             0),
    8677             :     /* For postgres_lookup_reserve() */
    8678          17 :     GNUNET_PQ_make_prepare ("lookup_reserve",
    8679             :                             "SELECT"
    8680             :                             " creation_time"
    8681             :                             ",expiration"
    8682             :                             ",merchant_initial_balance_val"
    8683             :                             ",merchant_initial_balance_frac"
    8684             :                             ",exchange_initial_balance_val"
    8685             :                             ",exchange_initial_balance_frac"
    8686             :                             ",tips_committed_val"
    8687             :                             ",tips_committed_frac"
    8688             :                             ",tips_picked_up_val"
    8689             :                             ",tips_picked_up_frac"
    8690             :                             ",reserve_priv IS NOT NULL AS active"
    8691             :                             ",exchange_url"
    8692             :                             ",payto_uri"
    8693             :                             " FROM merchant_tip_reserves"
    8694             :                             " FULL OUTER JOIN merchant_tip_reserve_keys USING (reserve_serial)"
    8695             :                             " WHERE reserve_pub = $2"
    8696             :                             "   AND merchant_serial ="
    8697             :                             "     (SELECT merchant_serial"
    8698             :                             "        FROM merchant_instances"
    8699             :                             "       WHERE merchant_id=$1)",
    8700             :                             2),
    8701             :     /* For postgres_lookup_reserve() */
    8702          17 :     GNUNET_PQ_make_prepare ("lookup_reserve_tips",
    8703             :                             "SELECT"
    8704             :                             " justification"
    8705             :                             ",tip_id"
    8706             :                             ",amount_val"
    8707             :                             ",amount_frac"
    8708             :                             " FROM merchant_tips"
    8709             :                             " WHERE reserve_serial ="
    8710             :                             "  (SELECT reserve_serial"
    8711             :                             "     FROM merchant_tip_reserves"
    8712             :                             "      WHERE reserve_pub=$2"
    8713             :                             "        AND merchant_serial ="
    8714             :                             "       (SELECT merchant_serial"
    8715             :                             "          FROM merchant_instances"
    8716             :                             "         WHERE merchant_id=$1))",
    8717             :                             2),
    8718             :     /* for postgres_delete_reserve() */
    8719          17 :     GNUNET_PQ_make_prepare ("delete_reserve",
    8720             :                             "DELETE"
    8721             :                             " FROM merchant_tip_reserve_keys"
    8722             :                             " WHERE reserve_serial="
    8723             :                             "   (SELECT reserve_serial"
    8724             :                             "      FROM merchant_tip_reserves"
    8725             :                             "       WHERE reserve_pub=$2"
    8726             :                             "         AND merchant_serial="
    8727             :                             "     (SELECT merchant_serial"
    8728             :                             "        FROM merchant_instances"
    8729             :                             "       WHERE merchant_id=$1))",
    8730             :                             2),
    8731             :     /* for postgres_purge_reserve() */
    8732          17 :     GNUNET_PQ_make_prepare ("purge_reserve",
    8733             :                             "DELETE"
    8734             :                             "   FROM merchant_tip_reserves"
    8735             :                             "  WHERE reserve_pub=$2"
    8736             :                             "    AND merchant_serial="
    8737             :                             "    (SELECT merchant_serial"
    8738             :                             "       FROM merchant_instances"
    8739             :                             "      WHERE merchant_id=$1)",
    8740             :                             2),
    8741             :     /* For postgres_authorize_tip() */
    8742          17 :     GNUNET_PQ_make_prepare ("lookup_reserve_for_tip",
    8743             :                             "SELECT"
    8744             :                             " reserve_pub"
    8745             :                             ",expiration"
    8746             :                             ",exchange_initial_balance_val"
    8747             :                             ",exchange_initial_balance_frac"
    8748             :                             ",tips_committed_val"
    8749             :                             ",tips_committed_frac"
    8750             :                             " FROM merchant_tip_reserves"
    8751             :                             " WHERE"
    8752             :                             "   merchant_serial ="
    8753             :                             "     (SELECT merchant_serial"
    8754             :                             "        FROM merchant_instances"
    8755             :                             "       WHERE merchant_id=$1)",
    8756             :                             1),
    8757             : 
    8758             :     /* For postgres_authorize_tip() */
    8759          17 :     GNUNET_PQ_make_prepare ("lookup_reserve_status",
    8760             :                             "SELECT"
    8761             :                             " expiration"
    8762             :                             ",exchange_initial_balance_val"
    8763             :                             ",exchange_initial_balance_frac"
    8764             :                             ",tips_committed_val"
    8765             :                             ",tips_committed_frac"
    8766             :                             " FROM merchant_tip_reserves"
    8767             :                             " WHERE reserve_pub = $2"
    8768             :                             "   AND merchant_serial ="
    8769             :                             "     (SELECT merchant_serial"
    8770             :                             "        FROM merchant_instances"
    8771             :                             "       WHERE merchant_id=$1)",
    8772             :                             2),
    8773             :     /* For postgres_authorize_tip() */
    8774          17 :     GNUNET_PQ_make_prepare ("update_reserve_tips_committed",
    8775             :                             "UPDATE merchant_tip_reserves SET"
    8776             :                             " tips_committed_val=$3"
    8777             :                             ",tips_committed_frac=$4"
    8778             :                             " WHERE reserve_pub = $2"
    8779             :                             "   AND merchant_serial ="
    8780             :                             "     (SELECT merchant_serial"
    8781             :                             "        FROM merchant_instances"
    8782             :                             "       WHERE merchant_id=$1)",
    8783             :                             4),
    8784             :     /* For postgres_authorize_tip() */
    8785          17 :     GNUNET_PQ_make_prepare ("insert_tip",
    8786             :                             "INSERT INTO merchant_tips"
    8787             :                             "(reserve_serial"
    8788             :                             ",tip_id"
    8789             :                             ",justification"
    8790             :                             ",next_url"
    8791             :                             ",expiration"
    8792             :                             ",amount_val"
    8793             :                             ",amount_frac"
    8794             :                             ") "
    8795             :                             "SELECT"
    8796             :                             " reserve_serial, $3, $4, $5, $6, $7, $8"
    8797             :                             " FROM merchant_tip_reserves"
    8798             :                             " WHERE reserve_pub=$2"
    8799             :                             "  AND merchant_serial = "
    8800             :                             "     (SELECT merchant_serial"
    8801             :                             "        FROM merchant_instances"
    8802             :                             "       WHERE merchant_id=$1)",
    8803             :                             8),
    8804             :     /* For postgres_lookup_pickup() */
    8805          17 :     GNUNET_PQ_make_prepare ("lookup_pickup",
    8806             :                             "SELECT"
    8807             :                             " exchange_url"
    8808             :                             ",reserve_priv"
    8809             :                             ",pickup_serial"
    8810             :                             " FROM merchant_tip_pickups"
    8811             :                             " JOIN merchant_tips USING (tip_serial)"
    8812             :                             " JOIN merchant_tip_reserves USING (reserve_serial)"
    8813             :                             " JOIN merchant_tip_reserve_keys USING (reserve_serial)"
    8814             :                             " WHERE pickup_id = $3"
    8815             :                             "   AND tip_id = $2"
    8816             :                             "   AND merchant_serial ="
    8817             :                             "     (SELECT merchant_serial"
    8818             :                             "        FROM merchant_instances"
    8819             :                             "       WHERE merchant_id=$1)",
    8820             :                             2),
    8821             :     /* For postgres_lookup_pickup() */
    8822          17 :     GNUNET_PQ_make_prepare ("lookup_pickup_signatures",
    8823             :                             "SELECT"
    8824             :                             " coin_offset"
    8825             :                             ",blind_sig"
    8826             :                             " FROM merchant_tip_pickup_signatures"
    8827             :                             " WHERE pickup_serial = $1",
    8828             :                             1),
    8829             : 
    8830             :     /* For postgres_lookup_tip() */
    8831          17 :     GNUNET_PQ_make_prepare ("lookup_tip",
    8832             :                             "SELECT"
    8833             :                             " amount_val"
    8834             :                             ",amount_frac"
    8835             :                             ",picked_up_val"
    8836             :                             ",picked_up_frac"
    8837             :                             ",merchant_tips.expiration"
    8838             :                             ",exchange_url"
    8839             :                             ",reserve_priv"
    8840             :                             " FROM merchant_tips"
    8841             :                             " JOIN merchant_tip_reserves USING (reserve_serial)"
    8842             :                             " JOIN merchant_tip_reserve_keys USING (reserve_serial)"
    8843             :                             " WHERE tip_id = $2"
    8844             :                             "   AND merchant_serial ="
    8845             :                             "     (SELECT merchant_serial"
    8846             :                             "        FROM merchant_instances"
    8847             :                             "       WHERE merchant_id=$1)",
    8848             :                             2),
    8849             :     /* For postgres_lookup_tip() */
    8850          17 :     GNUNET_PQ_make_prepare ("lookup_tips_inc",
    8851             :                             "SELECT"
    8852             :                             " tip_serial"
    8853             :                             ",tip_id"
    8854             :                             ",amount_val"
    8855             :                             ",amount_frac"
    8856             :                             ",CAST($4 as BIGINT)" /* otherwise $4 is unused and Postgres unhappy */
    8857             :                             ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
    8858             :                             " FROM merchant_tips"
    8859             :                             " JOIN merchant_tip_reserves USING (reserve_serial)"
    8860             :                             " WHERE merchant_serial ="
    8861             :                             "     (SELECT merchant_serial"
    8862             :                             "        FROM merchant_instances"
    8863             :                             "       WHERE merchant_id=$1)"
    8864             :                             "   AND"
    8865             :                             "    tip_serial > $3"
    8866