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

Generated by: LCOV version 1.14