gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-merchant] branch master updated: fix #5733 by migrat


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant] branch master updated: fix #5733 by migrating to new GNUnet PQ API
Date: Fri, 11 Oct 2019 23:36:20 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new 37bd689  fix #5733 by migrating to new GNUnet PQ API
37bd689 is described below

commit 37bd6899d24b234534926af5e40b112eb4565baa
Author: Christian Grothoff <address@hidden>
AuthorDate: Fri Oct 11 23:35:49 2019 +0200

    fix #5733 by migrating to new GNUnet PQ API
---
 src/backend/taler-merchant-httpd.c         |    8 -
 src/backenddb/plugin_merchantdb_postgres.c | 2000 ++++++++++++++--------------
 src/backenddb/test_merchantdb.c            |    4 +-
 src/include/taler_merchantdb_plugin.h      |    9 -
 src/merchant-tools/taler-merchant-dbinit.c |   10 +-
 5 files changed, 987 insertions(+), 1044 deletions(-)

diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index 47c3e34..bc784cd 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -1452,14 +1452,6 @@ run (void *cls,
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  if (GNUNET_OK !=
-      db->initialize (db->cls))
-  {
-    GNUNET_break (0);
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-
 
   {
     const char *choices[] = {"tcp",
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index fc5e2d1..b2a4df6 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -54,6 +54,8 @@
 #define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, \
                                         amountp) 
TALER_PQ_result_spec_amount_nbo ( \
     field,pg->currency,amountp)
+
+
 /**
  * Wrapper macro to add the currency from the plugin's state
  * when fetching amounts from the database.
@@ -75,7 +77,7 @@ struct PostgresClosure
   /**
    * Postgres connection handle.
    */
-  PGconn *conn;
+  struct GNUNET_PQ_Context *conn;
 
   /**
    * Which currency do we deal in?
@@ -140,698 +142,83 @@ postgres_drop_tables (void *cls)
 
 
 /**
- * Initialize merchant tables
+ * Check that the database connection is still up.
  *
- * @param cls closure our `struct Plugin`
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ * @param pg connection to check
+ */
+static void
+check_connection (struct PostgresClosure *pg)
+{
+  GNUNET_PQ_reconnect_if_down (pg->conn);
+}
+
+
+/**
+ * Do a pre-flight check that we are not in an uncommitted transaction.
+ * If we are, try to commit the previous transaction and output a warning.
+ * Does not return anything, as we will continue regardless of the outcome.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ */
+static void
+postgres_preflight (void *cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_ExecuteStatement es[] = {
+    GNUNET_PQ_make_execute ("COMMIT"),
+    GNUNET_PQ_EXECUTE_STATEMENT_END
+  };
+
+  if (NULL == pg->transaction_name)
+    return; /* all good */
+  if (GNUNET_OK ==
+      GNUNET_PQ_exec_statements (pg->conn,
+                                 es))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "BUG: Preflight check committed transaction `%s'!\n",
+                pg->transaction_name);
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "BUG: Preflight check failed to commit transaction `%s'!\n",
+                pg->transaction_name);
+  }
+  pg->transaction_name = NULL;
+}
+
+
+/**
+ * Start a transaction.
+ *
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param name unique name identifying the transaction (for debugging),
+ *             must point to a constant
+ * @return #GNUNET_OK on success
  */
 static int
-postgres_initialize (void *cls)
+postgres_start (void *cls,
+                const char *name)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_ExecuteStatement es[] = {
-    /* Orders created by the frontend, not signed or given a nonce yet.
-       The contract terms will change (nonce will be added) when moved to the
-       contract terms table */
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_orders ("
-                            "order_id VARCHAR NOT NULL"
-                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
-                            ",contract_terms BYTEA NOT NULL"
-                            ",timestamp INT8 NOT NULL"
-                            ",PRIMARY KEY (order_id, merchant_pub)"
-                            ");"),
-    /* Offers we made to customers */
-    GNUNET_PQ_make_execute (
-      "CREATE TABLE IF NOT EXISTS merchant_contract_terms ("
-      "order_id VARCHAR NOT NULL"
-      ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
-      ",contract_terms BYTEA NOT NULL"
-      ",h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)"
-      ",timestamp INT8 NOT NULL"
-      ",row_id BIGSERIAL UNIQUE"
-      ",paid boolean DEFAULT FALSE NOT NULL"
-      ",PRIMARY KEY (order_id, merchant_pub)"
-      ",UNIQUE (h_contract_terms, merchant_pub)"
-      ");"),
-    /* Table with the proofs for each coin we deposited at the exchange */
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_deposits ("
-                            " h_contract_terms BYTEA NOT NULL"
-                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
-                            ",coin_pub BYTEA NOT NULL CHECK 
(LENGTH(coin_pub)=32)"
-                            ",exchange_url VARCHAR NOT NULL"
-                            ",amount_with_fee_val INT8 NOT NULL"
-                            ",amount_with_fee_frac INT4 NOT NULL"
-                            ",deposit_fee_val INT8 NOT NULL"
-                            ",deposit_fee_frac INT4 NOT NULL"
-                            ",refund_fee_val INT8 NOT NULL"
-                            ",refund_fee_frac INT4 NOT NULL"
-                            ",wire_fee_val INT8 NOT NULL"
-                            ",wire_fee_frac INT4 NOT NULL"
-                            ",signkey_pub BYTEA NOT NULL CHECK 
(LENGTH(signkey_pub)=32)"
-                            ",exchange_proof BYTEA NOT NULL"
-                            ",PRIMARY KEY (h_contract_terms, coin_pub)"
-                            ",FOREIGN KEY (h_contract_terms, merchant_pub) 
REFERENCES merchant_contract_terms (h_contract_terms, merchant_pub)"
-                            ");"),
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_proofs ("
-                            " exchange_url VARCHAR NOT NULL"
-                            ",wtid BYTEA CHECK (LENGTH(wtid)=32)"
-                            ",execution_time INT8 NOT NULL"
-                            ",signkey_pub BYTEA NOT NULL CHECK 
(LENGTH(signkey_pub)=32)"
-                            ",proof BYTEA NOT NULL"
-                            ",PRIMARY KEY (wtid, exchange_url)"
-                            ");"),
-    /* Note that h_contract_terms + coin_pub may actually be unknown to
-       us, e.g. someone else deposits something for us at the exchange.
-       Hence those cannot be foreign keys into deposits/transactions! */
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_transfers ("
-                            " h_contract_terms BYTEA NOT NULL"
-                            ",coin_pub BYTEA NOT NULL CHECK 
(LENGTH(coin_pub)=32)"
-                            ",wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)"
-                            ",PRIMARY KEY (h_contract_terms, coin_pub)"
-                            ");"),
-    GNUNET_PQ_make_try_execute (
-      "CREATE INDEX IF NOT EXISTS merchant_transfers_by_coin"
-      " ON merchant_transfers (h_contract_terms, coin_pub)"),
-    GNUNET_PQ_make_try_execute (
-      "CREATE INDEX IF NOT EXISTS merchant_transfers_by_wtid"
-      " ON merchant_transfers (wtid)"),
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS exchange_wire_fees ("
-                            " exchange_pub BYTEA NOT NULL CHECK 
(length(exchange_pub)=32)"
-                            ",h_wire_method BYTEA NOT NULL CHECK 
(length(h_wire_method)=64)"
-                            ",wire_fee_val INT8 NOT NULL"
-                            ",wire_fee_frac INT4 NOT NULL"
-                            ",closing_fee_val INT8 NOT NULL"
-                            ",closing_fee_frac INT4 NOT NULL"
-                            ",start_date INT8 NOT NULL"
-                            ",end_date INT8 NOT NULL"
-                            ",exchange_sig BYTEA NOT NULL CHECK 
(length(exchange_sig)=64)"
-                            ",PRIMARY KEY 
(exchange_pub,h_wire_method,start_date,end_date)"
-                            ");"),
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_refunds ("
-                            " rtransaction_id BIGSERIAL UNIQUE"
-                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
-                            ",h_contract_terms BYTEA NOT NULL"
-                            ",coin_pub BYTEA NOT NULL CHECK 
(LENGTH(coin_pub)=32)"
-                            ",reason VARCHAR NOT NULL"
-                            ",refund_amount_val INT8 NOT NULL"
-                            ",refund_amount_frac INT4 NOT NULL"
-                            ",refund_fee_val INT8 NOT NULL"
-                            ",refund_fee_frac INT4 NOT NULL"
-                            ");"),
-    /* balances of the reserves available for tips */
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tip_reserves 
("
-                            " reserve_priv BYTEA NOT NULL CHECK 
(LENGTH(reserve_priv)=32)"
-                            ",expiration INT8 NOT NULL"
-                            ",balance_val INT8 NOT NULL"
-                            ",balance_frac INT4 NOT NULL"
-                            ",PRIMARY KEY (reserve_priv)"
-                            ");"),
-    /* table where we remember when tipping reserves where established / 
enabled */
-    GNUNET_PQ_make_execute (
-      "CREATE TABLE IF NOT EXISTS merchant_tip_reserve_credits ("
-      " reserve_priv BYTEA NOT NULL CHECK (LENGTH(reserve_priv)=32)"
-      ",credit_uuid BYTEA UNIQUE NOT NULL CHECK (LENGTH(credit_uuid)=64)"
-      ",timestamp INT8 NOT NULL"
-      ",amount_val INT8 NOT NULL"
-      ",amount_frac INT4 NOT NULL"
-      ",PRIMARY KEY (credit_uuid)"
-      ");"),
-    /* tips that have been authorized */
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tips ("
-                            " reserve_priv BYTEA NOT NULL CHECK 
(LENGTH(reserve_priv)=32)"
-                            ",tip_id BYTEA NOT NULL CHECK (LENGTH(tip_id)=64)"
-                            ",exchange_url VARCHAR NOT NULL"
-                            ",justification VARCHAR NOT NULL"
-                            ",extra BYTEA NOT NULL"
-                            ",timestamp INT8 NOT NULL"
-                            ",amount_val INT8 NOT NULL" /* overall tip amount 
*/
-                            ",amount_frac INT4 NOT NULL"
-                            ",left_val INT8 NOT NULL" /* tip amount not yet 
picked up */
-                            ",left_frac INT4 NOT NULL"
-                            ",PRIMARY KEY (tip_id)"
-                            ");"),
-    /* tips that have been picked up */
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tip_pickups ("
-                            " tip_id BYTEA NOT NULL REFERENCES merchant_tips 
(tip_id) ON DELETE CASCADE"
-                            ",pickup_id BYTEA NOT NULL CHECK 
(LENGTH(pickup_id)=64)"
-                            ",amount_val INT8 NOT NULL"
-                            ",amount_frac INT4 NOT NULL"
-                            ",PRIMARY KEY (pickup_id)"
-                            ");"),
-    /* sessions and their order_id/fulfillment_url mapping */
-    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_session_info 
("
-                            " session_id VARCHAR NOT NULL"
-                            ",fulfillment_url VARCHAR NOT NULL"
-                            ",order_id VARCHAR NOT NULL"
-                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
-                            ",timestamp INT8 NOT NULL"
-                            ",PRIMARY KEY (session_id, fulfillment_url, 
merchant_pub)"
-                            ",UNIQUE (session_id, fulfillment_url, order_id, 
merchant_pub)"
-                            ");"),
+    GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
     GNUNET_PQ_EXECUTE_STATEMENT_END
   };
-  struct GNUNET_PQ_PreparedStatement ps[] = {
-    GNUNET_PQ_make_prepare ("insert_deposit",
-                            "INSERT INTO merchant_deposits"
-                            "(h_contract_terms"
-                            ",merchant_pub"
-                            ",coin_pub"
-                            ",exchange_url"
-                            ",amount_with_fee_val"
-                            ",amount_with_fee_frac"
-                            ",deposit_fee_val"
-                            ",deposit_fee_frac"
-                            ",refund_fee_val"
-                            ",refund_fee_frac"
-                            ",wire_fee_val"
-                            ",wire_fee_frac"
-                            ",signkey_pub"
-                            ",exchange_proof) VALUES "
-                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, 
$12, $13, $14)",
-                            14),
-    GNUNET_PQ_make_prepare ("insert_transfer",
-                            "INSERT INTO merchant_transfers"
-                            "(h_contract_terms"
-                            ",coin_pub"
-                            ",wtid) VALUES "
-                            "($1, $2, $3)",
-                            3),
-    GNUNET_PQ_make_prepare ("insert_refund",
-                            "INSERT INTO merchant_refunds"
-                            "(merchant_pub"
-                            ",h_contract_terms"
-                            ",coin_pub"
-                            ",reason"
-                            ",refund_amount_val"
-                            ",refund_amount_frac"
-                            ",refund_fee_val"
-                            ",refund_fee_frac"
-                            ") VALUES"
-                            "($1, $2, $3, $4, $5, $6, $7, $8)",
-                            8),
-    GNUNET_PQ_make_prepare ("insert_proof",
-                            "INSERT INTO merchant_proofs"
-                            "(exchange_url"
-                            ",wtid"
-                            ",execution_time"
-                            ",signkey_pub"
-                            ",proof) VALUES "
-                            "($1, $2, $3, $4, $5)",
-                            5),
-    GNUNET_PQ_make_prepare ("insert_contract_terms",
-                            "INSERT INTO merchant_contract_terms"
-                            "(order_id"
-                            ",merchant_pub"
-                            ",timestamp"
-                            ",contract_terms"
-                            ",h_contract_terms)"
-                            " VALUES "
-                            "($1, $2, $3, $4, $5)",
-                            5),
-    GNUNET_PQ_make_prepare ("insert_order",
-                            "INSERT INTO merchant_orders"
-                            "(order_id"
-                            ",merchant_pub"
-                            ",timestamp"
-                            ",contract_terms)"
-                            " VALUES "
-                            "($1, $2, $3, $4)",
-                            4),
-    GNUNET_PQ_make_prepare ("insert_session_info",
-                            "INSERT INTO merchant_session_info"
-                            "(session_id"
-                            ",fulfillment_url"
-                            ",order_id"
-                            ",merchant_pub"
-                            ",timestamp)"
-                            " VALUES "
-                            "($1, $2, $3, $4, $5)",
-                            5),
-    GNUNET_PQ_make_prepare ("mark_proposal_paid",
-                            "UPDATE merchant_contract_terms SET"
-                            " paid=TRUE"
-                            " WHERE h_contract_terms=$1"
-                            " AND merchant_pub=$2",
-                            2),
-    GNUNET_PQ_make_prepare ("insert_wire_fee",
-                            "INSERT INTO exchange_wire_fees"
-                            "(exchange_pub"
-                            ",h_wire_method"
-                            ",wire_fee_val"
-                            ",wire_fee_frac"
-                            ",closing_fee_val"
-                            ",closing_fee_frac"
-                            ",start_date"
-                            ",end_date"
-                            ",exchange_sig)"
-                            " VALUES "
-                            "($1, $2, $3, $4, $5, $6, $7, $8, $9)",
-                            9),
-    GNUNET_PQ_make_prepare ("lookup_wire_fee",
-                            "SELECT"
-                            " wire_fee_val"
-                            ",wire_fee_frac"
-                            ",closing_fee_val"
-                            ",closing_fee_frac"
-                            ",start_date"
-                            ",end_date"
-                            ",exchange_sig"
-                            " FROM exchange_wire_fees"
-                            " WHERE exchange_pub=$1"
-                            "   AND h_wire_method=$2"
-                            "   AND start_date <= $3"
-                            "   AND end_date > $3",
-                            1),
-    GNUNET_PQ_make_prepare ("find_contract_terms_from_hash",
-                            "SELECT"
-                            " contract_terms"
-                            " FROM merchant_contract_terms"
-                            " WHERE h_contract_terms=$1"
-                            "   AND merchant_pub=$2",
-                            2),
-    GNUNET_PQ_make_prepare ("find_paid_contract_terms_from_hash",
-                            "SELECT"
-                            " contract_terms"
-                            " FROM merchant_contract_terms"
-                            " WHERE h_contract_terms=$1"
-                            "   AND merchant_pub=$2"
-                            "   AND paid=TRUE",
-                            2),
-    GNUNET_PQ_make_prepare ("end_transaction",
-                            "COMMIT",
-                            0),
-
-    GNUNET_PQ_make_prepare ("find_refunds",
-                            "SELECT"
-                            " refund_amount_val"
-                            ",refund_amount_frac"
-                            " FROM merchant_refunds"
-                            " WHERE coin_pub=$1",
-                            1),
-    GNUNET_PQ_make_prepare ("find_contract_terms_history",
-                            "SELECT"
-                            " contract_terms"
-                            " FROM merchant_contract_terms"
-                            " WHERE"
-                            " order_id=$1"
-                            " AND merchant_pub=$2"
-                            " AND paid=TRUE",
-                            2),
-    GNUNET_PQ_make_prepare ("find_contract_terms",
-                            "SELECT"
-                            " contract_terms"
-                            " FROM merchant_contract_terms"
-                            " WHERE"
-                            " order_id=$1"
-                            " AND merchant_pub=$2",
-                            2),
-    GNUNET_PQ_make_prepare ("find_order",
-                            "SELECT"
-                            " contract_terms"
-                            " FROM merchant_orders"
-                            " WHERE"
-                            " order_id=$1"
-                            " AND merchant_pub=$2",
-                            2),
-    GNUNET_PQ_make_prepare ("find_session_info",
-                            "SELECT"
-                            " order_id"
-                            " FROM merchant_session_info"
-                            " WHERE"
-                            " fulfillment_url=$1"
-                            " AND session_id=$2"
-                            " AND merchant_pub=$3",
-                            2),
-    GNUNET_PQ_make_prepare ("find_contract_terms_by_date",
-                            "SELECT"
-                            " contract_terms"
-                            ",order_id"
-                            ",row_id"
-                            " FROM merchant_contract_terms"
-                            " WHERE"
-                            " timestamp<$1"
-                            " AND merchant_pub=$2"
-                            " AND paid=TRUE"
-                            " ORDER BY row_id DESC, timestamp DESC"
-                            " LIMIT $3",
-                            3),
-    GNUNET_PQ_make_prepare ("find_refunds_from_contract_terms_hash",
-                            "SELECT"
-                            " coin_pub"
-                            ",rtransaction_id"
-                            ",refund_amount_val"
-                            ",refund_amount_frac"
-                            ",refund_fee_val"
-                            ",refund_fee_frac"
-                            ",reason"
-                            " FROM merchant_refunds"
-                            " WHERE merchant_pub=$1"
-                            " AND h_contract_terms=$2",
-                            2),
-    GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_asc",
-                            "SELECT"
-                            " contract_terms"
-                            ",order_id"
-                            ",row_id"
-                            " FROM merchant_contract_terms"
-                            " WHERE"
-                            " timestamp>$1"
-                            " AND merchant_pub=$2"
-                            " AND row_id>$3"
-                            " AND paid=TRUE"
-                            " ORDER BY row_id ASC, timestamp ASC"
-                            " LIMIT $4",
-                            4),
-    GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range",
-                            "SELECT"
-                            " contract_terms"
-                            ",order_id"
-                            ",row_id"
-                            " FROM merchant_contract_terms"
-                            " WHERE"
-                            " timestamp>$1"
-                            " AND merchant_pub=$2"
-                            " AND row_id>$3"
-                            " AND paid=TRUE"
-                            " ORDER BY row_id DESC, timestamp DESC"
-                            " LIMIT $4",
-                            4),
-    GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_past_asc",
-                            "SELECT"
-                            " contract_terms"
-                            ",order_id"
-                            ",row_id"
-                            " FROM merchant_contract_terms"
-                            " WHERE"
-                            " timestamp<$1"
-                            " AND merchant_pub=$2"
-                            " AND row_id<$3"
-                            " AND paid=TRUE"
-                            " ORDER BY row_id ASC, timestamp ASC"
-                            " LIMIT $4",
-                            4),
-    GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_past",
-                            "SELECT"
-                            " contract_terms"
-                            ",order_id"
-                            ",row_id"
-                            " FROM merchant_contract_terms"
-                            " WHERE"
-                            " timestamp<$1"
-                            " AND merchant_pub=$2"
-                            " AND row_id<$3"
-                            " AND paid=TRUE"
-                            " ORDER BY row_id DESC, timestamp DESC"
-                            " LIMIT $4",
-                            4),
-    GNUNET_PQ_make_prepare ("find_deposits",
-                            "SELECT"
-                            " coin_pub"
-                            ",exchange_url"
-                            ",amount_with_fee_val"
-                            ",amount_with_fee_frac"
-                            ",deposit_fee_val"
-                            ",deposit_fee_frac"
-                            ",refund_fee_val"
-                            ",refund_fee_frac"
-                            ",wire_fee_val"
-                            ",wire_fee_frac"
-                            ",exchange_proof"
-                            " FROM merchant_deposits"
-                            " WHERE h_contract_terms=$1"
-                            " AND merchant_pub=$2",
-                            2),
-    GNUNET_PQ_make_prepare ("find_deposits_by_hash_and_coin",
-                            "SELECT"
-                            " amount_with_fee_val"
-                            ",amount_with_fee_frac"
-                            ",deposit_fee_val"
-                            ",deposit_fee_frac"
-                            ",refund_fee_val"
-                            ",refund_fee_frac"
-                            ",wire_fee_val"
-                            ",wire_fee_frac"
-                            ",exchange_url"
-                            ",exchange_proof"
-                            " FROM merchant_deposits"
-                            " WHERE h_contract_terms=$1"
-                            " AND merchant_pub=$2"
-                            " AND coin_pub=$3",
-                            3),
-    GNUNET_PQ_make_prepare ("find_transfers_by_hash",
-                            "SELECT"
-                            " coin_pub"
-                            ",wtid"
-                            ",merchant_proofs.execution_time"
-                            ",merchant_proofs.proof"
-                            " FROM merchant_transfers"
-                            "   JOIN merchant_proofs USING (wtid)"
-                            " WHERE h_contract_terms=$1",
-                            1),
-    GNUNET_PQ_make_prepare ("find_deposits_by_wtid",
-                            "SELECT"
-                            " merchant_transfers.h_contract_terms"
-                            ",merchant_transfers.coin_pub"
-                            ",merchant_deposits.amount_with_fee_val"
-                            ",merchant_deposits.amount_with_fee_frac"
-                            ",merchant_deposits.deposit_fee_val"
-                            ",merchant_deposits.deposit_fee_frac"
-                            ",merchant_deposits.refund_fee_val"
-                            ",merchant_deposits.refund_fee_frac"
-                            ",merchant_deposits.wire_fee_val"
-                            ",merchant_deposits.wire_fee_frac"
-                            ",merchant_deposits.exchange_url"
-                            ",merchant_deposits.exchange_proof"
-                            " FROM merchant_transfers"
-                            "   JOIN merchant_deposits"
-                            "     ON (merchant_deposits.h_contract_terms = 
merchant_transfers.h_contract_terms"
-                            "       AND"
-                            "         merchant_deposits.coin_pub = 
merchant_transfers.coin_pub)"
-                            " WHERE wtid=$1",
-                            1),
-    GNUNET_PQ_make_prepare ("find_proof_by_wtid",
-                            "SELECT"
-                            " proof"
-                            " FROM merchant_proofs"
-                            " WHERE wtid=$1"
-                            "  AND exchange_url=$2",
-                            2),
-    GNUNET_PQ_make_prepare ("lookup_tip_reserve_balance",
-                            "SELECT"
-                            " expiration"
-                            ",balance_val"
-                            ",balance_frac"
-                            " FROM merchant_tip_reserves"
-                            " WHERE reserve_priv=$1",
-                            1),
-    GNUNET_PQ_make_prepare ("find_tip_authorizations",
-                            "SELECT"
-                            " amount_val"
-                            ",amount_frac"
-                            ",justification"
-                            ",extra"
-                            ",tip_id"
-                            " FROM merchant_tips"
-                            " WHERE reserve_priv=$1",
-                            1),
-    GNUNET_PQ_make_prepare ("update_tip_reserve_balance",
-                            "UPDATE merchant_tip_reserves SET"
-                            " expiration=$2"
-                            ",balance_val=$3"
-                            ",balance_frac=$4"
-                            " WHERE reserve_priv=$1",
-                            4),
-    GNUNET_PQ_make_prepare ("insert_tip_reserve_balance",
-                            "INSERT INTO merchant_tip_reserves"
-                            "(reserve_priv"
-                            ",expiration"
-                            ",balance_val"
-                            ",balance_frac"
-                            ") VALUES "
-                            "($1, $2, $3, $4)",
-                            4),
-    GNUNET_PQ_make_prepare ("insert_tip_justification",
-                            "INSERT INTO merchant_tips"
-                            "(reserve_priv"
-                            ",tip_id"
-                            ",exchange_url"
-                            ",justification"
-                            ",extra"
-                            ",timestamp"
-                            ",amount_val"
-                            ",amount_frac"
-                            ",left_val"
-                            ",left_frac"
-                            ") VALUES "
-                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
-                            10),
-    GNUNET_PQ_make_prepare ("lookup_reserve_by_tip_id",
-                            "SELECT"
-                            " reserve_priv"
-                            ",left_val"
-                            ",left_frac"
-                            " FROM merchant_tips"
-                            " WHERE tip_id=$1",
-                            1),
-    GNUNET_PQ_make_prepare ("lookup_amount_by_pickup",
-                            "SELECT"
-                            " amount_val"
-                            ",amount_frac"
-                            " FROM merchant_tip_pickups"
-                            " WHERE pickup_id=$1"
-                            " AND tip_id=$2",
-                            2),
-    GNUNET_PQ_make_prepare ("find_tip_by_id",
-                            "SELECT"
-                            " exchange_url"
-                            ",extra"
-                            ",timestamp"
-                            ",amount_val"
-                            ",amount_frac"
-                            ",left_val"
-                            ",left_frac"
-                            " FROM merchant_tips"
-                            " WHERE tip_id=$1",
-                            1),
-    GNUNET_PQ_make_prepare ("update_tip_balance",
-                            "UPDATE merchant_tips SET"
-                            " left_val=$2"
-                            ",left_frac=$3"
-                            " WHERE tip_id=$1",
-                            3),
-    GNUNET_PQ_make_prepare ("insert_pickup_id",
-                            "INSERT INTO merchant_tip_pickups"
-                            "(tip_id"
-                            ",pickup_id"
-                            ",amount_val"
-                            ",amount_frac"
-                            ") VALUES "
-                            "($1, $2, $3, $4)",
-                            4),
-    GNUNET_PQ_make_prepare ("insert_tip_credit_uuid",
-                            "INSERT INTO merchant_tip_reserve_credits"
-                            "(reserve_priv"
-                            ",credit_uuid"
-                            ",timestamp"
-                            ",amount_val"
-                            ",amount_frac"
-                            ") VALUES "
-                            "($1, $2, $3, $4, $5)",
-                            5),
-    GNUNET_PQ_make_prepare ("lookup_tip_credit_uuid",
-                            "SELECT 1 "
-                            "FROM merchant_tip_reserve_credits "
-                            "WHERE credit_uuid=$1 AND reserve_priv=$2",
-                            2),
-    GNUNET_PQ_PREPARED_STATEMENT_END
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_PQ_exec_statements (pg->conn,
-                                 es))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      GNUNET_PQ_prepare_statements (pg->conn,
-                                    ps))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Check that the database connection is still up.
- *
- * @param pg connection to check
- */
-static void
-check_connection (struct PostgresClosure *pg)
-{
-  if (CONNECTION_BAD != PQstatus (pg->conn))
-    return;
-  PQfinish (pg->conn);
-  pg->conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
-                                         "merchantdb-postgres");
-  GNUNET_break (NULL != pg->conn);
-  GNUNET_break (GNUNET_OK ==
-                postgres_initialize (pg));
-}
-
-
-/**
- * Do a pre-flight check that we are not in an uncommitted transaction.
- * If we are, try to commit the previous transaction and output a warning.
- * Does not return anything, as we will continue regardless of the outcome.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- */
-static void
-postgres_preflight (void *cls)
-{
-  struct PostgresClosure *pg = cls;
-  PGresult *result;
-  ExecStatusType status;
-
-  if (NULL == pg->transaction_name)
-    return; /* all good */
-  result = PQexec (pg->conn,
-                   "COMMIT");
-  status = PQresultStatus (result);
-  if (PGRES_COMMAND_OK == status)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "BUG: Preflight check committed transaction `%s'!\n",
-                pg->transaction_name);
-  }
-  else
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "BUG: Preflight check failed to commit transaction `%s'!\n",
-                pg->transaction_name);
-  }
-  pg->transaction_name = NULL;
-  PQclear (result);
-}
-
-
-/**
- * Start a transaction.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param name unique name identifying the transaction (for debugging),
- *             must point to a constant
- * @return #GNUNET_OK on success
- */
-static int
-postgres_start (void *cls,
-                const char *name)
-{
-  struct PostgresClosure *pg = cls;
-  PGresult *result;
-  ExecStatusType ex;
 
   check_connection (pg);
   postgres_preflight (pg);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Starting merchant DB transaction\n");
-  result = PQexec (pg->conn,
-                   "START TRANSACTION ISOLATION LEVEL SERIALIZABLE");
-  if (PGRES_COMMAND_OK !=
-      (ex = PQresultStatus (result)))
+  if (GNUNET_OK !=
+      GNUNET_PQ_exec_statements (pg->conn,
+                                 es))
   {
-    TALER_LOG_ERROR ("Failed to start transaction (%s): %s\n",
-                     PQresStatus (ex),
-                     PQerrorMessage (pg->conn));
+    TALER_LOG_ERROR ("Failed to start transaction\n");
     GNUNET_break (0);
-    PQclear (result);
     return GNUNET_SYSERR;
   }
-  PQclear (result);
   pg->transaction_name = name;
   return GNUNET_OK;
 }
@@ -847,15 +234,16 @@ static void
 postgres_rollback (void *cls)
 {
   struct PostgresClosure *pg = cls;
-  PGresult *result;
+  struct GNUNET_PQ_ExecuteStatement es[] = {
+    GNUNET_PQ_make_execute ("ROLLBACK"),
+    GNUNET_PQ_EXECUTE_STATEMENT_END
+  };
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Rolling back merchant DB transaction\n");
-  result = PQexec (pg->conn,
-                   "ROLLBACK");
-  GNUNET_break (PGRES_COMMAND_OK ==
-                PQresultStatus (result));
-  PQclear (result);
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_PQ_exec_statements (pg->conn,
+                                           es));
   pg->transaction_name = NULL;
 }
 
@@ -3282,393 +2670,969 @@ postgres_authorize_tip_TR (void *cls,
   struct GNUNET_TIME_Absolute old_expiration;
   struct TALER_Amount old_balance;
   struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_absolute_time ("expiration",
-                                         &old_expiration),
-    TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
-                                 &old_balance),
+    GNUNET_PQ_result_spec_absolute_time ("expiration",
+                                         &old_expiration),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
+                                 &old_balance),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_Amount new_balance;
+  unsigned int retries;
+
+  retries = 0;
+  check_connection (pg);
+RETRY:
+  if (MAX_RETRIES < ++retries)
+    return TALER_EC_TIP_AUTHORIZE_DB_SOFT_ERROR;
+  if (GNUNET_OK !=
+      postgres_start (pg,
+                      "authorize tip"))
+  {
+    GNUNET_break (0);
+    return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+  }
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 "lookup_tip_reserve_balance",
+                                                 params,
+                                                 rs);
+  if (0 >= qs)
+  {
+    /* reserve unknown */
+    postgres_rollback (pg);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      goto RETRY;
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
+    return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+  }
+  if (0 == GNUNET_TIME_absolute_get_remaining (old_expiration).rel_value_us)
+  {
+    /* reserve expired, can't be used */
+    postgres_rollback (pg);
+    return TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED;
+  }
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&new_balance,
+                             &old_balance,
+                             amount))
+  {
+    /* insufficient funds left in reserve */
+    postgres_rollback (pg);
+    return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
+  }
+  /* Update reserve balance */
+  {
+    struct GNUNET_PQ_QueryParam params[] = {
+      GNUNET_PQ_query_param_auto_from_type (reserve_priv),
+      GNUNET_PQ_query_param_absolute_time (&old_expiration),
+      TALER_PQ_query_param_amount (&new_balance),
+      GNUNET_PQ_query_param_end
+    };
+
+    qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "update_tip_reserve_balance",
+                                             params);
+    if (0 > qs)
+    {
+      postgres_rollback (pg);
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        goto RETRY;
+      return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+    }
+  }
+  /* Generate and store tip ID */
+  *expiration = old_expiration;
+  GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG,
+                                    tip_id);
+  {
+    struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+    struct GNUNET_PQ_QueryParam params[] = {
+      GNUNET_PQ_query_param_auto_from_type (reserve_priv),
+      GNUNET_PQ_query_param_auto_from_type (tip_id),
+      GNUNET_PQ_query_param_string (exchange_url),
+      GNUNET_PQ_query_param_string (justification),
+      TALER_PQ_query_param_json (extra),
+      GNUNET_PQ_query_param_absolute_time (&now),
+      TALER_PQ_query_param_amount (amount), /* overall amount */
+      TALER_PQ_query_param_amount (amount), /* amount left */
+      GNUNET_PQ_query_param_end
+    };
+
+    (void) GNUNET_TIME_round_abs (&now);
+    qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_tip_justification",
+                                             params);
+    if (0 > qs)
+    {
+      postgres_rollback (pg);
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        goto RETRY;
+      return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+    }
+  }
+  qs = postgres_commit (pg);
+  if (0 <= qs)
+    return TALER_EC_NONE; /* success! */
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    goto RETRY;
+  return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+}
+
+
+/**
+ * Find out tip authorization details associated with @a tip_id
+ *
+ * @param cls closure, typically a connection to the d
+ * @param tip_id the unique ID for the tip
+ * @param[out] exchange_url set to the URL of the exchange (unless NULL)
+ * @param[out] extra extra data to pass to the wallet (unless NULL)
+ * @param[out] amount set to the authorized amount (unless NULL)
+ * @param[out] amount_left set to the amount left (unless NULL)
+ * @param[out] timestamp set to the timestamp of the tip authorization (unless 
NULL)
+ * @return transaction status, usually
+ *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
+ *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_tip_by_id (void *cls,
+                           const struct GNUNET_HashCode *tip_id,
+                           char **exchange_url,
+                           json_t **extra,
+                           struct TALER_Amount *amount,
+                           struct TALER_Amount *amount_left,
+                           struct GNUNET_TIME_Absolute *timestamp)
+{
+  struct PostgresClosure *pg = cls;
+  char *res_exchange_url;
+  json_t *res_extra;
+  struct TALER_Amount res_amount;
+  struct TALER_Amount res_amount_left;
+  struct GNUNET_TIME_Absolute res_timestamp;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (tip_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_string ("exchange_url",
+                                  &res_exchange_url),
+    GNUNET_PQ_result_spec_absolute_time ("timestamp",
+                                         &res_timestamp),
+    TALER_PQ_result_spec_json ("extra",
+                               &res_extra),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                 &res_amount),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("left",
+                                 &res_amount_left),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                 "find_tip_by_id",
+                                                 params,
+                                                 rs);
+  if (0 >= qs)
+  {
+    if (NULL != exchange_url)
+      *exchange_url = NULL;
+    return qs;
+  }
+  if (NULL != exchange_url)
+    *exchange_url = strdup (res_exchange_url);
+  if (NULL != amount)
+    *amount = res_amount;
+  if (NULL != amount_left)
+    *amount_left = res_amount_left;
+  if (NULL != timestamp)
+    *timestamp = res_timestamp;
+  if (NULL != extra)
+  {
+    json_incref (res_extra);
+    *extra = res_extra;
+  }
+  GNUNET_PQ_cleanup_result (rs);
+  return qs;
+}
+
+
+/**
+ * Pickup a tip over @a amount using pickup id @a pickup_id.
+ *
+ * @param cls closure, typically a connection to the db
+ * @param amount how high is the amount picked up (with fees)
+ * @param tip_id the unique ID from the tip authorization
+ * @param pickup_id the unique ID identifying the pick up operation
+ *        (to allow replays, hash over the coin envelope and denomination key)
+ * @param[out] reserve_priv which reserve key to use to sign
+ * @return taler error code
+ *      #TALER_EC_TIP_PICKUP_ID_UNKNOWN if @a tip_id is unknown
+ *      #TALER_EC_TIP_PICKUP_NO_FUNDS if @a tip_id has insufficient funds left
+ *      #TALER_EC_TIP_PICKUP_DB_ERROR_HARD on hard database errors
+ *      #TALER_EC_TIP_PICKUP_AMOUNT_CHANGED if @a amount is different for 
known @a pickup_id
+ *      #TALER_EC_TIP_PICKUP_DB_ERROR_SOFT on soft database errors (client 
should retry)
+ *      #TALER_EC_NONE upon success (@a reserve_priv was set)
+ */
+static enum TALER_ErrorCode
+postgres_pickup_tip_TR (void *cls,
+                        const struct TALER_Amount *amount,
+                        const struct GNUNET_HashCode *tip_id,
+                        const struct GNUNET_HashCode *pickup_id,
+                        struct TALER_ReservePrivateKeyP *reserve_priv)
+{
+  struct PostgresClosure *pg = cls;
+  struct TALER_Amount left_amount;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (tip_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
+                                          reserve_priv),
+    TALER_PQ_RESULT_SPEC_AMOUNT ("left",
+                                 &left_amount),
     GNUNET_PQ_result_spec_end
   };
   enum GNUNET_DB_QueryStatus qs;
-  struct TALER_Amount new_balance;
   unsigned int retries;
 
   retries = 0;
   check_connection (pg);
 RETRY:
   if (MAX_RETRIES < ++retries)
-    return TALER_EC_TIP_AUTHORIZE_DB_SOFT_ERROR;
+    return TALER_EC_TIP_PICKUP_DB_ERROR_SOFT;
   if (GNUNET_OK !=
       postgres_start (pg,
-                      "authorize tip"))
+                      "pickup tip"))
   {
     GNUNET_break (0);
-    return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+    return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
   }
   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                 "lookup_tip_reserve_balance",
+                                                 "lookup_reserve_by_tip_id",
                                                  params,
                                                  rs);
   if (0 >= qs)
   {
-    /* reserve unknown */
+    /* tip ID unknown */
+    memset (reserve_priv,
+            0,
+            sizeof (*reserve_priv));
     postgres_rollback (pg);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      return TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN;
     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
       goto RETRY;
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-      return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
-    return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
-  }
-  if (0 == GNUNET_TIME_absolute_get_remaining (old_expiration).rel_value_us)
-  {
-    /* reserve expired, can't be used */
-    postgres_rollback (pg);
-    return TALER_EC_TIP_AUTHORIZE_RESERVE_EXPIRED;
-  }
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (&new_balance,
-                             &old_balance,
-                             amount))
-  {
-    /* insufficient funds left in reserve */
-    postgres_rollback (pg);
-    return TALER_EC_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
+    return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
   }
-  /* Update reserve balance */
+
+  /* Check if pickup_id already exists */
   {
+    struct TALER_Amount existing_amount;
     struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (reserve_priv),
-      GNUNET_PQ_query_param_absolute_time (&old_expiration),
-      TALER_PQ_query_param_amount (&new_balance),
+      GNUNET_PQ_query_param_auto_from_type (pickup_id),
+      GNUNET_PQ_query_param_auto_from_type (tip_id),
       GNUNET_PQ_query_param_end
     };
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+                                   &existing_amount),
+      GNUNET_PQ_result_spec_end
+    };
 
-    qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "update_tip_reserve_balance",
-                                             params);
+    qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "lookup_amount_by_pickup",
+                                                   params,
+                                                   rs);
     if (0 > qs)
     {
+      /* DB error */
+      memset (reserve_priv,
+              0,
+              sizeof (*reserve_priv));
       postgres_rollback (pg);
       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
         goto RETRY;
-      return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+      return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
+    }
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+    {
+      if (0 !=
+          TALER_amount_cmp (&existing_amount,
+                            amount))
+      {
+        GNUNET_break_op (0);
+        postgres_rollback (pg);
+        return TALER_EC_TIP_PICKUP_AMOUNT_CHANGED;
+      }
+      postgres_commit (pg);
+      return TALER_EC_NONE; /* we are done! */
     }
   }
-  /* Generate and store tip ID */
-  *expiration = old_expiration;
-  GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG,
-                                    tip_id);
+
+  /* Calculate new balance */
   {
-    struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (reserve_priv),
-      GNUNET_PQ_query_param_auto_from_type (tip_id),
-      GNUNET_PQ_query_param_string (exchange_url),
-      GNUNET_PQ_query_param_string (justification),
-      TALER_PQ_query_param_json (extra),
-      GNUNET_PQ_query_param_absolute_time (&now),
-      TALER_PQ_query_param_amount (amount), /* overall amount */
-      TALER_PQ_query_param_amount (amount), /* amount left */
-      GNUNET_PQ_query_param_end
-    };
+    struct TALER_Amount new_left;
 
-    (void) GNUNET_TIME_round_abs (&now);
-    qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "insert_tip_justification",
-                                             params);
-    if (0 > qs)
+    if (GNUNET_SYSERR ==
+        TALER_amount_subtract (&new_left,
+                               &left_amount,
+                               amount))
     {
+      /* attempt to take more tips than the tipping amount */
+      GNUNET_break_op (0);
+      memset (reserve_priv,
+              0,
+              sizeof (*reserve_priv));
       postgres_rollback (pg);
-      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-        goto RETRY;
-      return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+      return TALER_EC_TIP_PICKUP_NO_FUNDS;
+    }
+
+    /* Update DB: update balance */
+    {
+      struct GNUNET_PQ_QueryParam params[] = {
+        GNUNET_PQ_query_param_auto_from_type (tip_id),
+        TALER_PQ_query_param_amount (&new_left),
+        GNUNET_PQ_query_param_end
+      };
+
+      qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                               "update_tip_balance",
+                                               params);
+      if (0 > qs)
+      {
+        postgres_rollback (pg);
+        memset (reserve_priv,
+                0,
+                sizeof (*reserve_priv));
+        if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+          goto RETRY;
+        return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
+      }
+    }
+
+    /* Update DB: remember pickup_id */
+    {
+      struct GNUNET_PQ_QueryParam params[] = {
+        GNUNET_PQ_query_param_auto_from_type (tip_id),
+        GNUNET_PQ_query_param_auto_from_type (pickup_id),
+        TALER_PQ_query_param_amount (amount),
+        GNUNET_PQ_query_param_end
+      };
+
+      qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                               "insert_pickup_id",
+                                               params);
+      if (0 > qs)
+      {
+        postgres_rollback (pg);
+        memset (reserve_priv,
+                0,
+                sizeof (*reserve_priv));
+        if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+          goto RETRY;
+        return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
+      }
     }
   }
   qs = postgres_commit (pg);
   if (0 <= qs)
-    return TALER_EC_NONE; /* success! */
+    return TALER_EC_NONE; /* success  */
   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     goto RETRY;
-  return TALER_EC_TIP_AUTHORIZE_DB_HARD_ERROR;
+  return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
 }
 
 
 /**
- * Find out tip authorization details associated with @a tip_id
+ * Initialize Postgres database subsystem.
  *
- * @param cls closure, typically a connection to the d
- * @param tip_id the unique ID for the tip
- * @param[out] exchange_url set to the URL of the exchange (unless NULL)
- * @param[out] extra extra data to pass to the wallet (unless NULL)
- * @param[out] amount set to the authorized amount (unless NULL)
- * @param[out] amount_left set to the amount left (unless NULL)
- * @param[out] timestamp set to the timestamp of the tip authorization (unless 
NULL)
- * @return transaction status, usually
- *      #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
- *      #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
+ * @param cls a configuration instance
+ * @return NULL on error, otherwise a `struct TALER_MERCHANTDB_Plugin`
  */
-static enum GNUNET_DB_QueryStatus
-postgres_lookup_tip_by_id (void *cls,
-                           const struct GNUNET_HashCode *tip_id,
-                           char **exchange_url,
-                           json_t **extra,
-                           struct TALER_Amount *amount,
-                           struct TALER_Amount *amount_left,
-                           struct GNUNET_TIME_Absolute *timestamp)
+void *
+libtaler_plugin_merchantdb_postgres_init (void *cls)
 {
-  struct PostgresClosure *pg = cls;
-  char *res_exchange_url;
-  json_t *res_extra;
-  struct TALER_Amount res_amount;
-  struct TALER_Amount res_amount_left;
-  struct GNUNET_TIME_Absolute res_timestamp;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (tip_id),
-    GNUNET_PQ_query_param_end
-  };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_string ("exchange_url",
-                                  &res_exchange_url),
-    GNUNET_PQ_result_spec_absolute_time ("timestamp",
-                                         &res_timestamp),
-    TALER_PQ_result_spec_json ("extra",
-                               &res_extra),
-    TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
-                                 &res_amount),
-    TALER_PQ_RESULT_SPEC_AMOUNT ("left",
-                                 &res_amount_left),
-    GNUNET_PQ_result_spec_end
+  struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+  struct PostgresClosure *pg;
+  struct TALER_MERCHANTDB_Plugin *plugin;
+  const char *ec;
+  struct GNUNET_PQ_ExecuteStatement es[] = {
+    /* Orders created by the frontend, not signed or given a nonce yet.
+       The contract terms will change (nonce will be added) when moved to the
+       contract terms table */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_orders ("
+                            "order_id VARCHAR NOT NULL"
+                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
+                            ",contract_terms BYTEA NOT NULL"
+                            ",timestamp INT8 NOT NULL"
+                            ",PRIMARY KEY (order_id, merchant_pub)"
+                            ");"),
+    /* Offers we made to customers */
+    GNUNET_PQ_make_execute (
+      "CREATE TABLE IF NOT EXISTS merchant_contract_terms ("
+      "order_id VARCHAR NOT NULL"
+      ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
+      ",contract_terms BYTEA NOT NULL"
+      ",h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)"
+      ",timestamp INT8 NOT NULL"
+      ",row_id BIGSERIAL UNIQUE"
+      ",paid boolean DEFAULT FALSE NOT NULL"
+      ",PRIMARY KEY (order_id, merchant_pub)"
+      ",UNIQUE (h_contract_terms, merchant_pub)"
+      ");"),
+    /* Table with the proofs for each coin we deposited at the exchange */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_deposits ("
+                            " h_contract_terms BYTEA NOT NULL"
+                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
+                            ",coin_pub BYTEA NOT NULL CHECK 
(LENGTH(coin_pub)=32)"
+                            ",exchange_url VARCHAR NOT NULL"
+                            ",amount_with_fee_val INT8 NOT NULL"
+                            ",amount_with_fee_frac INT4 NOT NULL"
+                            ",deposit_fee_val INT8 NOT NULL"
+                            ",deposit_fee_frac INT4 NOT NULL"
+                            ",refund_fee_val INT8 NOT NULL"
+                            ",refund_fee_frac INT4 NOT NULL"
+                            ",wire_fee_val INT8 NOT NULL"
+                            ",wire_fee_frac INT4 NOT NULL"
+                            ",signkey_pub BYTEA NOT NULL CHECK 
(LENGTH(signkey_pub)=32)"
+                            ",exchange_proof BYTEA NOT NULL"
+                            ",PRIMARY KEY (h_contract_terms, coin_pub)"
+                            ",FOREIGN KEY (h_contract_terms, merchant_pub) 
REFERENCES merchant_contract_terms (h_contract_terms, merchant_pub)"
+                            ");"),
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_proofs ("
+                            " exchange_url VARCHAR NOT NULL"
+                            ",wtid BYTEA CHECK (LENGTH(wtid)=32)"
+                            ",execution_time INT8 NOT NULL"
+                            ",signkey_pub BYTEA NOT NULL CHECK 
(LENGTH(signkey_pub)=32)"
+                            ",proof BYTEA NOT NULL"
+                            ",PRIMARY KEY (wtid, exchange_url)"
+                            ");"),
+    /* Note that h_contract_terms + coin_pub may actually be unknown to
+       us, e.g. someone else deposits something for us at the exchange.
+       Hence those cannot be foreign keys into deposits/transactions! */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_transfers ("
+                            " h_contract_terms BYTEA NOT NULL"
+                            ",coin_pub BYTEA NOT NULL CHECK 
(LENGTH(coin_pub)=32)"
+                            ",wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)"
+                            ",PRIMARY KEY (h_contract_terms, coin_pub)"
+                            ");"),
+    GNUNET_PQ_make_try_execute (
+      "CREATE INDEX IF NOT EXISTS merchant_transfers_by_coin"
+      " ON merchant_transfers (h_contract_terms, coin_pub)"),
+    GNUNET_PQ_make_try_execute (
+      "CREATE INDEX IF NOT EXISTS merchant_transfers_by_wtid"
+      " ON merchant_transfers (wtid)"),
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS exchange_wire_fees ("
+                            " exchange_pub BYTEA NOT NULL CHECK 
(length(exchange_pub)=32)"
+                            ",h_wire_method BYTEA NOT NULL CHECK 
(length(h_wire_method)=64)"
+                            ",wire_fee_val INT8 NOT NULL"
+                            ",wire_fee_frac INT4 NOT NULL"
+                            ",closing_fee_val INT8 NOT NULL"
+                            ",closing_fee_frac INT4 NOT NULL"
+                            ",start_date INT8 NOT NULL"
+                            ",end_date INT8 NOT NULL"
+                            ",exchange_sig BYTEA NOT NULL CHECK 
(length(exchange_sig)=64)"
+                            ",PRIMARY KEY 
(exchange_pub,h_wire_method,start_date,end_date)"
+                            ");"),
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_refunds ("
+                            " rtransaction_id BIGSERIAL UNIQUE"
+                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
+                            ",h_contract_terms BYTEA NOT NULL"
+                            ",coin_pub BYTEA NOT NULL CHECK 
(LENGTH(coin_pub)=32)"
+                            ",reason VARCHAR NOT NULL"
+                            ",refund_amount_val INT8 NOT NULL"
+                            ",refund_amount_frac INT4 NOT NULL"
+                            ",refund_fee_val INT8 NOT NULL"
+                            ",refund_fee_frac INT4 NOT NULL"
+                            ");"),
+    /* balances of the reserves available for tips */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tip_reserves 
("
+                            " reserve_priv BYTEA NOT NULL CHECK 
(LENGTH(reserve_priv)=32)"
+                            ",expiration INT8 NOT NULL"
+                            ",balance_val INT8 NOT NULL"
+                            ",balance_frac INT4 NOT NULL"
+                            ",PRIMARY KEY (reserve_priv)"
+                            ");"),
+    /* table where we remember when tipping reserves where established / 
enabled */
+    GNUNET_PQ_make_execute (
+      "CREATE TABLE IF NOT EXISTS merchant_tip_reserve_credits ("
+      " reserve_priv BYTEA NOT NULL CHECK (LENGTH(reserve_priv)=32)"
+      ",credit_uuid BYTEA UNIQUE NOT NULL CHECK (LENGTH(credit_uuid)=64)"
+      ",timestamp INT8 NOT NULL"
+      ",amount_val INT8 NOT NULL"
+      ",amount_frac INT4 NOT NULL"
+      ",PRIMARY KEY (credit_uuid)"
+      ");"),
+    /* tips that have been authorized */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tips ("
+                            " reserve_priv BYTEA NOT NULL CHECK 
(LENGTH(reserve_priv)=32)"
+                            ",tip_id BYTEA NOT NULL CHECK (LENGTH(tip_id)=64)"
+                            ",exchange_url VARCHAR NOT NULL"
+                            ",justification VARCHAR NOT NULL"
+                            ",extra BYTEA NOT NULL"
+                            ",timestamp INT8 NOT NULL"
+                            ",amount_val INT8 NOT NULL" /* overall tip amount 
*/
+                            ",amount_frac INT4 NOT NULL"
+                            ",left_val INT8 NOT NULL" /* tip amount not yet 
picked up */
+                            ",left_frac INT4 NOT NULL"
+                            ",PRIMARY KEY (tip_id)"
+                            ");"),
+    /* tips that have been picked up */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_tip_pickups ("
+                            " tip_id BYTEA NOT NULL REFERENCES merchant_tips 
(tip_id) ON DELETE CASCADE"
+                            ",pickup_id BYTEA NOT NULL CHECK 
(LENGTH(pickup_id)=64)"
+                            ",amount_val INT8 NOT NULL"
+                            ",amount_frac INT4 NOT NULL"
+                            ",PRIMARY KEY (pickup_id)"
+                            ");"),
+    /* sessions and their order_id/fulfillment_url mapping */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_session_info 
("
+                            " session_id VARCHAR NOT NULL"
+                            ",fulfillment_url VARCHAR NOT NULL"
+                            ",order_id VARCHAR NOT NULL"
+                            ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
+                            ",timestamp INT8 NOT NULL"
+                            ",PRIMARY KEY (session_id, fulfillment_url, 
merchant_pub)"
+                            ",UNIQUE (session_id, fulfillment_url, order_id, 
merchant_pub)"
+                            ");"),
+    GNUNET_PQ_EXECUTE_STATEMENT_END
   };
-  enum GNUNET_DB_QueryStatus qs;
-
-  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                 "find_tip_by_id",
-                                                 params,
-                                                 rs);
-  if (0 >= qs)
-  {
-    if (NULL != exchange_url)
-      *exchange_url = NULL;
-    return qs;
-  }
-  if (NULL != exchange_url)
-    *exchange_url = strdup (res_exchange_url);
-  if (NULL != amount)
-    *amount = res_amount;
-  if (NULL != amount_left)
-    *amount_left = res_amount_left;
-  if (NULL != timestamp)
-    *timestamp = res_timestamp;
-  if (NULL != extra)
-  {
-    json_incref (res_extra);
-    *extra = res_extra;
-  }
-  GNUNET_PQ_cleanup_result (rs);
-  return qs;
-}
-
+  struct GNUNET_PQ_PreparedStatement ps[] = {
+    GNUNET_PQ_make_prepare ("insert_deposit",
+                            "INSERT INTO merchant_deposits"
+                            "(h_contract_terms"
+                            ",merchant_pub"
+                            ",coin_pub"
+                            ",exchange_url"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",deposit_fee_val"
+                            ",deposit_fee_frac"
+                            ",refund_fee_val"
+                            ",refund_fee_frac"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",signkey_pub"
+                            ",exchange_proof) VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, 
$12, $13, $14)",
+                            14),
+    GNUNET_PQ_make_prepare ("insert_transfer",
+                            "INSERT INTO merchant_transfers"
+                            "(h_contract_terms"
+                            ",coin_pub"
+                            ",wtid) VALUES "
+                            "($1, $2, $3)",
+                            3),
+    GNUNET_PQ_make_prepare ("insert_refund",
+                            "INSERT INTO merchant_refunds"
+                            "(merchant_pub"
+                            ",h_contract_terms"
+                            ",coin_pub"
+                            ",reason"
+                            ",refund_amount_val"
+                            ",refund_amount_frac"
+                            ",refund_fee_val"
+                            ",refund_fee_frac"
+                            ") VALUES"
+                            "($1, $2, $3, $4, $5, $6, $7, $8)",
+                            8),
+    GNUNET_PQ_make_prepare ("insert_proof",
+                            "INSERT INTO merchant_proofs"
+                            "(exchange_url"
+                            ",wtid"
+                            ",execution_time"
+                            ",signkey_pub"
+                            ",proof) VALUES "
+                            "($1, $2, $3, $4, $5)",
+                            5),
+    GNUNET_PQ_make_prepare ("insert_contract_terms",
+                            "INSERT INTO merchant_contract_terms"
+                            "(order_id"
+                            ",merchant_pub"
+                            ",timestamp"
+                            ",contract_terms"
+                            ",h_contract_terms)"
+                            " VALUES "
+                            "($1, $2, $3, $4, $5)",
+                            5),
+    GNUNET_PQ_make_prepare ("insert_order",
+                            "INSERT INTO merchant_orders"
+                            "(order_id"
+                            ",merchant_pub"
+                            ",timestamp"
+                            ",contract_terms)"
+                            " VALUES "
+                            "($1, $2, $3, $4)",
+                            4),
+    GNUNET_PQ_make_prepare ("insert_session_info",
+                            "INSERT INTO merchant_session_info"
+                            "(session_id"
+                            ",fulfillment_url"
+                            ",order_id"
+                            ",merchant_pub"
+                            ",timestamp)"
+                            " VALUES "
+                            "($1, $2, $3, $4, $5)",
+                            5),
+    GNUNET_PQ_make_prepare ("mark_proposal_paid",
+                            "UPDATE merchant_contract_terms SET"
+                            " paid=TRUE"
+                            " WHERE h_contract_terms=$1"
+                            " AND merchant_pub=$2",
+                            2),
+    GNUNET_PQ_make_prepare ("insert_wire_fee",
+                            "INSERT INTO exchange_wire_fees"
+                            "(exchange_pub"
+                            ",h_wire_method"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",closing_fee_val"
+                            ",closing_fee_frac"
+                            ",start_date"
+                            ",end_date"
+                            ",exchange_sig)"
+                            " VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9)",
+                            9),
+    GNUNET_PQ_make_prepare ("lookup_wire_fee",
+                            "SELECT"
+                            " wire_fee_val"
+                            ",wire_fee_frac"
+                            ",closing_fee_val"
+                            ",closing_fee_frac"
+                            ",start_date"
+                            ",end_date"
+                            ",exchange_sig"
+                            " FROM exchange_wire_fees"
+                            " WHERE exchange_pub=$1"
+                            "   AND h_wire_method=$2"
+                            "   AND start_date <= $3"
+                            "   AND end_date > $3",
+                            1),
+    GNUNET_PQ_make_prepare ("find_contract_terms_from_hash",
+                            "SELECT"
+                            " contract_terms"
+                            " FROM merchant_contract_terms"
+                            " WHERE h_contract_terms=$1"
+                            "   AND merchant_pub=$2",
+                            2),
+    GNUNET_PQ_make_prepare ("find_paid_contract_terms_from_hash",
+                            "SELECT"
+                            " contract_terms"
+                            " FROM merchant_contract_terms"
+                            " WHERE h_contract_terms=$1"
+                            "   AND merchant_pub=$2"
+                            "   AND paid=TRUE",
+                            2),
+    GNUNET_PQ_make_prepare ("end_transaction",
+                            "COMMIT",
+                            0),
 
-/**
- * Pickup a tip over @a amount using pickup id @a pickup_id.
- *
- * @param cls closure, typically a connection to the db
- * @param amount how high is the amount picked up (with fees)
- * @param tip_id the unique ID from the tip authorization
- * @param pickup_id the unique ID identifying the pick up operation
- *        (to allow replays, hash over the coin envelope and denomination key)
- * @param[out] reserve_priv which reserve key to use to sign
- * @return taler error code
- *      #TALER_EC_TIP_PICKUP_ID_UNKNOWN if @a tip_id is unknown
- *      #TALER_EC_TIP_PICKUP_NO_FUNDS if @a tip_id has insufficient funds left
- *      #TALER_EC_TIP_PICKUP_DB_ERROR_HARD on hard database errors
- *      #TALER_EC_TIP_PICKUP_AMOUNT_CHANGED if @a amount is different for 
known @a pickup_id
- *      #TALER_EC_TIP_PICKUP_DB_ERROR_SOFT on soft database errors (client 
should retry)
- *      #TALER_EC_NONE upon success (@a reserve_priv was set)
- */
-static enum TALER_ErrorCode
-postgres_pickup_tip_TR (void *cls,
-                        const struct TALER_Amount *amount,
-                        const struct GNUNET_HashCode *tip_id,
-                        const struct GNUNET_HashCode *pickup_id,
-                        struct TALER_ReservePrivateKeyP *reserve_priv)
-{
-  struct PostgresClosure *pg = cls;
-  struct TALER_Amount left_amount;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (tip_id),
-    GNUNET_PQ_query_param_end
-  };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
-                                          reserve_priv),
-    TALER_PQ_RESULT_SPEC_AMOUNT ("left",
-                                 &left_amount),
-    GNUNET_PQ_result_spec_end
+    GNUNET_PQ_make_prepare ("find_refunds",
+                            "SELECT"
+                            " refund_amount_val"
+                            ",refund_amount_frac"
+                            " FROM merchant_refunds"
+                            " WHERE coin_pub=$1",
+                            1),
+    GNUNET_PQ_make_prepare ("find_contract_terms_history",
+                            "SELECT"
+                            " contract_terms"
+                            " FROM merchant_contract_terms"
+                            " WHERE"
+                            " order_id=$1"
+                            " AND merchant_pub=$2"
+                            " AND paid=TRUE",
+                            2),
+    GNUNET_PQ_make_prepare ("find_contract_terms",
+                            "SELECT"
+                            " contract_terms"
+                            " FROM merchant_contract_terms"
+                            " WHERE"
+                            " order_id=$1"
+                            " AND merchant_pub=$2",
+                            2),
+    GNUNET_PQ_make_prepare ("find_order",
+                            "SELECT"
+                            " contract_terms"
+                            " FROM merchant_orders"
+                            " WHERE"
+                            " order_id=$1"
+                            " AND merchant_pub=$2",
+                            2),
+    GNUNET_PQ_make_prepare ("find_session_info",
+                            "SELECT"
+                            " order_id"
+                            " FROM merchant_session_info"
+                            " WHERE"
+                            " fulfillment_url=$1"
+                            " AND session_id=$2"
+                            " AND merchant_pub=$3",
+                            2),
+    GNUNET_PQ_make_prepare ("find_contract_terms_by_date",
+                            "SELECT"
+                            " contract_terms"
+                            ",order_id"
+                            ",row_id"
+                            " FROM merchant_contract_terms"
+                            " WHERE"
+                            " timestamp<$1"
+                            " AND merchant_pub=$2"
+                            " AND paid=TRUE"
+                            " ORDER BY row_id DESC, timestamp DESC"
+                            " LIMIT $3",
+                            3),
+    GNUNET_PQ_make_prepare ("find_refunds_from_contract_terms_hash",
+                            "SELECT"
+                            " coin_pub"
+                            ",rtransaction_id"
+                            ",refund_amount_val"
+                            ",refund_amount_frac"
+                            ",refund_fee_val"
+                            ",refund_fee_frac"
+                            ",reason"
+                            " FROM merchant_refunds"
+                            " WHERE merchant_pub=$1"
+                            " AND h_contract_terms=$2",
+                            2),
+    GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_asc",
+                            "SELECT"
+                            " contract_terms"
+                            ",order_id"
+                            ",row_id"
+                            " FROM merchant_contract_terms"
+                            " WHERE"
+                            " timestamp>$1"
+                            " AND merchant_pub=$2"
+                            " AND row_id>$3"
+                            " AND paid=TRUE"
+                            " ORDER BY row_id ASC, timestamp ASC"
+                            " LIMIT $4",
+                            4),
+    GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range",
+                            "SELECT"
+                            " contract_terms"
+                            ",order_id"
+                            ",row_id"
+                            " FROM merchant_contract_terms"
+                            " WHERE"
+                            " timestamp>$1"
+                            " AND merchant_pub=$2"
+                            " AND row_id>$3"
+                            " AND paid=TRUE"
+                            " ORDER BY row_id DESC, timestamp DESC"
+                            " LIMIT $4",
+                            4),
+    GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_past_asc",
+                            "SELECT"
+                            " contract_terms"
+                            ",order_id"
+                            ",row_id"
+                            " FROM merchant_contract_terms"
+                            " WHERE"
+                            " timestamp<$1"
+                            " AND merchant_pub=$2"
+                            " AND row_id<$3"
+                            " AND paid=TRUE"
+                            " ORDER BY row_id ASC, timestamp ASC"
+                            " LIMIT $4",
+                            4),
+    GNUNET_PQ_make_prepare ("find_contract_terms_by_date_and_range_past",
+                            "SELECT"
+                            " contract_terms"
+                            ",order_id"
+                            ",row_id"
+                            " FROM merchant_contract_terms"
+                            " WHERE"
+                            " timestamp<$1"
+                            " AND merchant_pub=$2"
+                            " AND row_id<$3"
+                            " AND paid=TRUE"
+                            " ORDER BY row_id DESC, timestamp DESC"
+                            " LIMIT $4",
+                            4),
+    GNUNET_PQ_make_prepare ("find_deposits",
+                            "SELECT"
+                            " coin_pub"
+                            ",exchange_url"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",deposit_fee_val"
+                            ",deposit_fee_frac"
+                            ",refund_fee_val"
+                            ",refund_fee_frac"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",exchange_proof"
+                            " FROM merchant_deposits"
+                            " WHERE h_contract_terms=$1"
+                            " AND merchant_pub=$2",
+                            2),
+    GNUNET_PQ_make_prepare ("find_deposits_by_hash_and_coin",
+                            "SELECT"
+                            " amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",deposit_fee_val"
+                            ",deposit_fee_frac"
+                            ",refund_fee_val"
+                            ",refund_fee_frac"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",exchange_url"
+                            ",exchange_proof"
+                            " FROM merchant_deposits"
+                            " WHERE h_contract_terms=$1"
+                            " AND merchant_pub=$2"
+                            " AND coin_pub=$3",
+                            3),
+    GNUNET_PQ_make_prepare ("find_transfers_by_hash",
+                            "SELECT"
+                            " coin_pub"
+                            ",wtid"
+                            ",merchant_proofs.execution_time"
+                            ",merchant_proofs.proof"
+                            " FROM merchant_transfers"
+                            "   JOIN merchant_proofs USING (wtid)"
+                            " WHERE h_contract_terms=$1",
+                            1),
+    GNUNET_PQ_make_prepare ("find_deposits_by_wtid",
+                            "SELECT"
+                            " merchant_transfers.h_contract_terms"
+                            ",merchant_transfers.coin_pub"
+                            ",merchant_deposits.amount_with_fee_val"
+                            ",merchant_deposits.amount_with_fee_frac"
+                            ",merchant_deposits.deposit_fee_val"
+                            ",merchant_deposits.deposit_fee_frac"
+                            ",merchant_deposits.refund_fee_val"
+                            ",merchant_deposits.refund_fee_frac"
+                            ",merchant_deposits.wire_fee_val"
+                            ",merchant_deposits.wire_fee_frac"
+                            ",merchant_deposits.exchange_url"
+                            ",merchant_deposits.exchange_proof"
+                            " FROM merchant_transfers"
+                            "   JOIN merchant_deposits"
+                            "     ON (merchant_deposits.h_contract_terms = 
merchant_transfers.h_contract_terms"
+                            "       AND"
+                            "         merchant_deposits.coin_pub = 
merchant_transfers.coin_pub)"
+                            " WHERE wtid=$1",
+                            1),
+    GNUNET_PQ_make_prepare ("find_proof_by_wtid",
+                            "SELECT"
+                            " proof"
+                            " FROM merchant_proofs"
+                            " WHERE wtid=$1"
+                            "  AND exchange_url=$2",
+                            2),
+    GNUNET_PQ_make_prepare ("lookup_tip_reserve_balance",
+                            "SELECT"
+                            " expiration"
+                            ",balance_val"
+                            ",balance_frac"
+                            " FROM merchant_tip_reserves"
+                            " WHERE reserve_priv=$1",
+                            1),
+    GNUNET_PQ_make_prepare ("find_tip_authorizations",
+                            "SELECT"
+                            " amount_val"
+                            ",amount_frac"
+                            ",justification"
+                            ",extra"
+                            ",tip_id"
+                            " FROM merchant_tips"
+                            " WHERE reserve_priv=$1",
+                            1),
+    GNUNET_PQ_make_prepare ("update_tip_reserve_balance",
+                            "UPDATE merchant_tip_reserves SET"
+                            " expiration=$2"
+                            ",balance_val=$3"
+                            ",balance_frac=$4"
+                            " WHERE reserve_priv=$1",
+                            4),
+    GNUNET_PQ_make_prepare ("insert_tip_reserve_balance",
+                            "INSERT INTO merchant_tip_reserves"
+                            "(reserve_priv"
+                            ",expiration"
+                            ",balance_val"
+                            ",balance_frac"
+                            ") VALUES "
+                            "($1, $2, $3, $4)",
+                            4),
+    GNUNET_PQ_make_prepare ("insert_tip_justification",
+                            "INSERT INTO merchant_tips"
+                            "(reserve_priv"
+                            ",tip_id"
+                            ",exchange_url"
+                            ",justification"
+                            ",extra"
+                            ",timestamp"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",left_val"
+                            ",left_frac"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
+                            10),
+    GNUNET_PQ_make_prepare ("lookup_reserve_by_tip_id",
+                            "SELECT"
+                            " reserve_priv"
+                            ",left_val"
+                            ",left_frac"
+                            " FROM merchant_tips"
+                            " WHERE tip_id=$1",
+                            1),
+    GNUNET_PQ_make_prepare ("lookup_amount_by_pickup",
+                            "SELECT"
+                            " amount_val"
+                            ",amount_frac"
+                            " FROM merchant_tip_pickups"
+                            " WHERE pickup_id=$1"
+                            " AND tip_id=$2",
+                            2),
+    GNUNET_PQ_make_prepare ("find_tip_by_id",
+                            "SELECT"
+                            " exchange_url"
+                            ",extra"
+                            ",timestamp"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",left_val"
+                            ",left_frac"
+                            " FROM merchant_tips"
+                            " WHERE tip_id=$1",
+                            1),
+    GNUNET_PQ_make_prepare ("update_tip_balance",
+                            "UPDATE merchant_tips SET"
+                            " left_val=$2"
+                            ",left_frac=$3"
+                            " WHERE tip_id=$1",
+                            3),
+    GNUNET_PQ_make_prepare ("insert_pickup_id",
+                            "INSERT INTO merchant_tip_pickups"
+                            "(tip_id"
+                            ",pickup_id"
+                            ",amount_val"
+                            ",amount_frac"
+                            ") VALUES "
+                            "($1, $2, $3, $4)",
+                            4),
+    GNUNET_PQ_make_prepare ("insert_tip_credit_uuid",
+                            "INSERT INTO merchant_tip_reserve_credits"
+                            "(reserve_priv"
+                            ",credit_uuid"
+                            ",timestamp"
+                            ",amount_val"
+                            ",amount_frac"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5)",
+                            5),
+    GNUNET_PQ_make_prepare ("lookup_tip_credit_uuid",
+                            "SELECT 1 "
+                            "FROM merchant_tip_reserve_credits "
+                            "WHERE credit_uuid=$1 AND reserve_priv=$2",
+                            2),
+    GNUNET_PQ_PREPARED_STATEMENT_END
   };
-  enum GNUNET_DB_QueryStatus qs;
-  unsigned int retries;
-
-  retries = 0;
-  check_connection (pg);
-RETRY:
-  if (MAX_RETRIES < ++retries)
-    return TALER_EC_TIP_PICKUP_DB_ERROR_SOFT;
-  if (GNUNET_OK !=
-      postgres_start (pg,
-                      "pickup tip"))
-  {
-    GNUNET_break (0);
-    return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
-  }
-  qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                 "lookup_reserve_by_tip_id",
-                                                 params,
-                                                 rs);
-  if (0 >= qs)
-  {
-    /* tip ID unknown */
-    memset (reserve_priv,
-            0,
-            sizeof (*reserve_priv));
-    postgres_rollback (pg);
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-      return TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN;
-    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-      goto RETRY;
-    return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
-  }
-
-  /* Check if pickup_id already exists */
-  {
-    struct TALER_Amount existing_amount;
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (pickup_id),
-      GNUNET_PQ_query_param_auto_from_type (tip_id),
-      GNUNET_PQ_query_param_end
-    };
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
-                                   &existing_amount),
-      GNUNET_PQ_result_spec_end
-    };
-
-    qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "lookup_amount_by_pickup",
-                                                   params,
-                                                   rs);
-    if (0 > qs)
-    {
-      /* DB error */
-      memset (reserve_priv,
-              0,
-              sizeof (*reserve_priv));
-      postgres_rollback (pg);
-      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-        goto RETRY;
-      return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
-    }
-    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
-    {
-      if (0 !=
-          TALER_amount_cmp (&existing_amount,
-                            amount))
-      {
-        GNUNET_break_op (0);
-        postgres_rollback (pg);
-        return TALER_EC_TIP_PICKUP_AMOUNT_CHANGED;
-      }
-      postgres_commit (pg);
-      return TALER_EC_NONE; /* we are done! */
-    }
-  }
-
-  /* Calculate new balance */
-  {
-    struct TALER_Amount new_left;
-
-    if (GNUNET_SYSERR ==
-        TALER_amount_subtract (&new_left,
-                               &left_amount,
-                               amount))
-    {
-      /* attempt to take more tips than the tipping amount */
-      GNUNET_break_op (0);
-      memset (reserve_priv,
-              0,
-              sizeof (*reserve_priv));
-      postgres_rollback (pg);
-      return TALER_EC_TIP_PICKUP_NO_FUNDS;
-    }
-
-    /* Update DB: update balance */
-    {
-      struct GNUNET_PQ_QueryParam params[] = {
-        GNUNET_PQ_query_param_auto_from_type (tip_id),
-        TALER_PQ_query_param_amount (&new_left),
-        GNUNET_PQ_query_param_end
-      };
-
-      qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                               "update_tip_balance",
-                                               params);
-      if (0 > qs)
-      {
-        postgres_rollback (pg);
-        memset (reserve_priv,
-                0,
-                sizeof (*reserve_priv));
-        if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-          goto RETRY;
-        return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
-      }
-    }
-
-    /* Update DB: remember pickup_id */
-    {
-      struct GNUNET_PQ_QueryParam params[] = {
-        GNUNET_PQ_query_param_auto_from_type (tip_id),
-        GNUNET_PQ_query_param_auto_from_type (pickup_id),
-        TALER_PQ_query_param_amount (amount),
-        GNUNET_PQ_query_param_end
-      };
-
-      qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                               "insert_pickup_id",
-                                               params);
-      if (0 > qs)
-      {
-        postgres_rollback (pg);
-        memset (reserve_priv,
-                0,
-                sizeof (*reserve_priv));
-        if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-          goto RETRY;
-        return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
-      }
-    }
-  }
-  qs = postgres_commit (pg);
-  if (0 <= qs)
-    return TALER_EC_NONE; /* success  */
-  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-    goto RETRY;
-  return TALER_EC_TIP_PICKUP_DB_ERROR_HARD;
-}
-
-
-/**
- * Initialize Postgres database subsystem.
- *
- * @param cls a configuration instance
- * @return NULL on error, otherwise a `struct TALER_MERCHANTDB_Plugin`
- */
-void *
-libtaler_plugin_merchantdb_postgres_init (void *cls)
-{
-  struct GNUNET_CONFIGURATION_Handle *cfg = cls;
-  struct PostgresClosure *pg;
-  struct TALER_MERCHANTDB_Plugin *plugin;
-  const char *ec;
 
   pg = GNUNET_new (struct PostgresClosure);
   ec = getenv ("TALER_MERCHANTDB_POSTGRES_CONFIG");
@@ -3694,7 +3658,9 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
   }
   pg->cfg = cfg;
   pg->conn = GNUNET_PQ_connect_with_cfg (cfg,
-                                         "merchantdb-postgres");
+                                         "merchantdb-postgres",
+                                         es,
+                                         ps);
   if (NULL == pg->conn)
   {
     GNUNET_free (pg);
@@ -3709,13 +3675,13 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "taler",
                                "CURRENCY");
+    GNUNET_PQ_disconnect (pg->conn);
     GNUNET_free (pg);
     return NULL;
   }
   plugin = GNUNET_new (struct TALER_MERCHANTDB_Plugin);
   plugin->cls = pg;
   plugin->drop_tables = &postgres_drop_tables;
-  plugin->initialize = &postgres_initialize;
   plugin->store_deposit = &postgres_store_deposit;
   plugin->store_coin_to_transfer = &postgres_store_coin_to_transfer;
   plugin->store_transfer_to_proof = &postgres_store_transfer_to_proof;
@@ -3772,7 +3738,7 @@ libtaler_plugin_merchantdb_postgres_done (void *cls)
   struct TALER_MERCHANTDB_Plugin *plugin = cls;
   struct PostgresClosure *pg = plugin->cls;
 
-  PQfinish (pg->conn);
+  GNUNET_PQ_disconnect (pg->conn);
   GNUNET_free (pg->currency);
   GNUNET_free (pg);
   GNUNET_free (plugin);
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index 5756bbb..1a569b1 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -749,7 +749,6 @@ run (void *cls)
     result = 77;
     return;
   }
-
   if (GNUNET_OK != plugin->drop_tables (plugin->cls))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -757,7 +756,8 @@ run (void *cls)
     result = 77;
     return;
   }
-  if (GNUNET_OK != plugin->initialize (plugin->cls))
+  TALER_MERCHANTDB_plugin_unload (plugin);
+  if (NULL == (plugin = TALER_MERCHANTDB_plugin_load (cfg)))
   {
     result = 77;
     return;
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index 59cd2db..e8a91b5 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -191,15 +191,6 @@ struct TALER_MERCHANTDB_Plugin
   int
   (*drop_tables) (void *cls);
 
-  /**
-   * Initialize merchant tables
-   *
-   * @param cls closure
-   * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
-   */
-  int
-  (*initialize) (void *cls);
-
   /**
    * Insert order into db.
    *
diff --git a/src/merchant-tools/taler-merchant-dbinit.c 
b/src/merchant-tools/taler-merchant-dbinit.c
index 2da6cd5..b5e85ee 100644
--- a/src/merchant-tools/taler-merchant-dbinit.c
+++ b/src/merchant-tools/taler-merchant-dbinit.c
@@ -60,17 +60,11 @@ run (void *cls,
     return;
   }
   if (reset_db)
-    (void) plugin->drop_tables (plugin->cls);
-  if (GNUNET_OK !=
-      plugin->initialize (plugin->cls))
   {
-    fprintf (stderr,
-             "Failed to initialize database.\n");
+    (void) plugin->drop_tables (plugin->cls);
     TALER_MERCHANTDB_plugin_unload (plugin);
-    global_ret = 1;
-    return;
+    plugin = TALER_MERCHANTDB_plugin_load (cfg);
   }
-
   TALER_MERCHANTDB_plugin_unload (plugin);
 }
 

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]