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