From d080e59e272e307b9ebc267f2c4dd2941cd79436 Mon Sep 17 00:00:00 2001
From: Christian Grothoff <christian@grothoff.org>
Date: Fri, 15 May 2015 17:24:27 +0200
Subject: [PATCH] resolve #3717

---
 src/include/taler_mintdb_plugin.h      |  4 ++-
 src/mint-tools/taler-mint-reservemod.c | 40 ++++++++++++++++++++------
 src/mintdb/plugin_mintdb_postgres.c    | 23 ++++++++++++---
 3 files changed, 53 insertions(+), 14 deletions(-)

diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h
index af641b186..1f4707b4d 100644
--- a/src/include/taler_mintdb_plugin.h
+++ b/src/include/taler_mintdb_plugin.h
@@ -685,7 +685,9 @@ struct TALER_MINTDB_Plugin
 
   /**
    * Insert a incoming transaction into reserves.  New reserves are
-   * also created through this function.
+   * also created through this function.  Note that this API call
+   * starts (and stops) its own transaction scope (so the application
+   * must not do so).
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param db the database connection handle
diff --git a/src/mint-tools/taler-mint-reservemod.c b/src/mint-tools/taler-mint-reservemod.c
index 8607c6dbf..645a2f32e 100644
--- a/src/mint-tools/taler-mint-reservemod.c
+++ b/src/mint-tools/taler-mint-reservemod.c
@@ -98,6 +98,9 @@ main (int argc, char *const *argv)
   {
     fprintf (stderr,
              "Mint directory not given\n");
+    GNUNET_free_non_null (add_str);
+    GNUNET_free_non_null (details);
+    GNUNET_free_non_null (reserve_pub_str);
     return 1;
   }
   if ((NULL == reserve_pub_str) ||
@@ -109,6 +112,9 @@ main (int argc, char *const *argv)
   {
     fprintf (stderr,
              "Parsing reserve key invalid\n");
+    GNUNET_free_non_null (add_str);
+    GNUNET_free_non_null (details);
+    GNUNET_free_non_null (reserve_pub_str);
     return 1;
   }
   if ( (NULL == add_str) ||
@@ -119,6 +125,9 @@ main (int argc, char *const *argv)
     fprintf (stderr,
              "Failed to parse currency amount `%s'\n",
              add_str);
+    GNUNET_free_non_null (add_str);
+    GNUNET_free_non_null (details);
+    GNUNET_free_non_null (reserve_pub_str);
     return 1;
   }
 
@@ -126,7 +135,9 @@ main (int argc, char *const *argv)
   {
     fprintf (stderr,
              "No wiring details given (justification required)\n");
-    return 1;
+   GNUNET_free_non_null (add_str);
+   GNUNET_free_non_null (reserve_pub_str);
+   return 1;
   }
 
   cfg = TALER_config_load (mint_directory);
@@ -134,7 +145,10 @@ main (int argc, char *const *argv)
   {
     fprintf (stderr,
              "Failed to load mint configuration\n");
-    return 1;
+    GNUNET_free_non_null (add_str);
+    GNUNET_free_non_null (details);
+    GNUNET_free_non_null (reserve_pub_str);
+   return 1;
   }
   ret = 1;
   if (NULL ==
@@ -154,24 +168,32 @@ main (int argc, char *const *argv)
     goto cleanup;
   }
   expiration = GNUNET_TIME_relative_to_absolute (RESERVE_EXPIRATION);
-  if (GNUNET_OK !=
-      plugin->reserves_in_insert (plugin->cls,
-                                  session,
-                                  &reserve_pub,
-                                  &add_value,
-                                  details,
-                                  expiration))
+  ret = plugin->reserves_in_insert (plugin->cls,
+				    session,
+				    &reserve_pub,
+				    &add_value,
+				    details,
+				    expiration);
+  if (GNUNET_SYSERR == ret)
   {
     fprintf (stderr,
              "Failed to update reserve.\n");
     goto cleanup;
   }
+  if (GNUNET_NO == ret)
+  {
+    fprintf (stderr,
+             "Record exists, reserve not updated.\n");
+  }
   ret = 0;
  cleanup:
   if (NULL != plugin)
     TALER_MINTDB_plugin_unload (plugin);
   if (NULL != cfg)
     GNUNET_CONFIGURATION_destroy (cfg);
+  GNUNET_free_non_null (add_str);
+  GNUNET_free_non_null (details);
+  GNUNET_free_non_null (reserve_pub_str);
   return ret;
 }
 
diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c
index 45599f6e9..baf94ddab 100644
--- a/src/mintdb/plugin_mintdb_postgres.c
+++ b/src/mintdb/plugin_mintdb_postgres.c
@@ -986,7 +986,8 @@ postgres_reserves_update (void *cls,
 
 /**
  * Insert a incoming transaction into reserves.  New reserves are also created
- * through this function.
+ * through this function.  Note that this API call starts (and stops) its
+ * own transaction scope (so the application must not do so).
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session the database connection handle
@@ -1025,9 +1026,8 @@ postgres_reserves_in_insert (void *cls,
                                          &reserve);
   if (GNUNET_SYSERR == reserve_exists)
   {
-    postgres_rollback (cls,
-                       session);
-    return GNUNET_SYSERR;
+    GNUNET_break (0);
+    goto rollback;
   }
   if (GNUNET_NO == reserve_exists)
   {
@@ -1084,6 +1084,21 @@ postgres_reserves_in_insert (void *cls,
                                    params);
   if (PGRES_COMMAND_OK != PQresultStatus(result))
   {
+    const char *efield;
+    
+    efield = PQresultErrorField (result,
+				 PG_DIAG_SQLSTATE);
+    if ( (PGRES_FATAL_ERROR == PQresultStatus(result)) &&
+	 (NULL != strstr ("23505", /* unique violation */
+			  efield)) )
+    {
+      /* This means we had the same reserve/justification/details
+	 before */
+      PQclear (result);
+      postgres_rollback (cls,
+			 session);
+      return GNUNET_NO;
+    }
     QUERY_ERR (result);
     goto rollback;
   }