commit b04ee597bb545670e2429bd3fce2f9c644fa1ce8
parent 019c04add75050e4c98e406ccbc1682ab2e7243e
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 5 Aug 2026 23:27:48 +0200
remove dead ON CONFLICT clause, ensure we initialize report_serial_id, always
Diffstat:
4 files changed, 56 insertions(+), 4 deletions(-)
diff --git a/src/backend/taler-merchant-httpd_post-private-reports.c b/src/backend/taler-merchant-httpd_post-private-reports.c
@@ -61,7 +61,7 @@ TMH_private_post_reports (const struct TMH_RequestHandler *rh,
NULL),
GNUNET_JSON_spec_end ()
};
- uint64_t report_id;
+ uint64_t report_id = 0;
(void) rh;
@@ -120,8 +120,10 @@ TMH_private_post_reports (const struct TMH_RequestHandler *rh,
frequency,
frequency_shift,
&report_id);
- if (qs < 0)
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
+ /* The INSERT either stores the row and returns its serial, or it
+ fails; anything else would leave @e report_id unset. */
GNUNET_break (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
diff --git a/src/backenddb/insert_report.c b/src/backenddb/insert_report.c
@@ -86,7 +86,6 @@ TALER_MERCHANTDB_insert_report (
",next_transmission)"
" VALUES ($1, $2, $3, $4,"
" $5, $6, $7, $8, $9)"
- " ON CONFLICT DO NOTHING"
" RETURNING report_serial;");
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"",
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
@@ -57,6 +57,7 @@
#include "merchant-database/insert_pending_webhook.h"
#include "merchant-database/insert_product.h"
#include "merchant-database/insert_refund_proof.h"
+#include "merchant-database/insert_report.h"
#include "merchant-database/insert_template.h"
#include "merchant-database/insert_transfer.h"
#include "merchant-database/insert_transfer_details.h"
@@ -858,6 +859,53 @@ cleanup:
/**
+ * Tests that storing a report always yields a fresh serial.
+ *
+ * 'merchant_reports' has no unique key other than the identity
+ * column 'report_serial' that the INSERT never supplies, so the
+ * 'ON CONFLICT DO NOTHING' the INSERT used to carry could never
+ * fire. Should a deduplicating key ever be introduced, the INSERT
+ * must report it (as 'no results') rather than silently return no
+ * serial: POST /private/reports would otherwise answer 200 OK with
+ * an uninitialised 'report_serial_id'.
+ *
+ * @param instance the instance to store the reports for.
+ * @return 0 on success, 1 otherwise.
+ */
+static int
+test_insert_report_serial (const struct InstanceData *instance)
+{
+ uint64_t report_ids[3];
+
+ TEST_SET_INSTANCE (instance->instance.id,
+ GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
+ for (unsigned int i = 0; i<3; i++)
+ {
+ report_ids[i] = 0;
+ TEST_COND_RET_ON_FAIL (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
+ TALER_MERCHANTDB_insert_report (
+ pg,
+ instance->instance.id,
+ "report-generator-test",
+ "a test report",
+ "text/plain",
+ "/private/orders",
+ "test@example.com",
+ GNUNET_TIME_UNIT_DAYS,
+ GNUNET_TIME_UNIT_ZERO,
+ &report_ids[i]),
+ "Insert report failed\n");
+ TEST_COND_RET_ON_FAIL (0 != report_ids[i],
+ "Insert report did not return a serial\n");
+ for (unsigned int j = 0; j<i; j++)
+ TEST_COND_RET_ON_FAIL (report_ids[i] != report_ids[j],
+ "Insert report returned a duplicate serial\n");
+ }
+ return 0;
+}
+
+
+/**
* Function that tests instances.
*
* @param cls closure with config
@@ -968,6 +1016,8 @@ run_test_instances (struct TestInstances_Closure *cls)
}
/* Test that a reconnect does not lose the per-instance search_path */
TEST_RET_ON_FAIL (test_reconnect_search_path (&cls->instances[0]));
+ /* Test that every stored report gets its own serial */
+ TEST_RET_ON_FAIL (test_insert_report_serial (&cls->instances[0]));
/* Test instance private key deletion */
TEST_RET_ON_FAIL (test_delete_instance_private_key (&cls->instances[0],
GNUNET_DB_STATUS_SUCCESS_ONE_RESULT));
diff --git a/src/include/merchant-database/insert_report.h b/src/include/merchant-database/insert_report.h
@@ -42,7 +42,8 @@ struct TALER_MERCHANTDB_PostgresContext;
* @param frequency report frequency
* @param frequency_shift how much to shift the report time from a
* multiple of the report @a frequency
- * @param[out] report_id serial number of the new pot
+ * @param[out] report_id serial number of the new pot, only set
+ * on #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
* @return database result code
*/
enum GNUNET_DB_QueryStatus