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