LCOV - code coverage report
Current view: top level - backenddb - plugin_merchantdb_postgres.c (source / functions) Hit Total Coverage
Test: GNU Taler coverage report Lines: 0 1918 0.0 %
Date: 2021-04-12 06:04:58 Functions: 0 100 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14