merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit bff270fa9e574150f4e54b0c69c56d12302daea4
parent 8288ed0bca06dd6795b5513ad870d31f7e87fb47
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 24 Sep 2025 12:56:27 +0200

ensure donaukeyupdate uses LEFT JOIN, fixes donau-minus-t test

Diffstat:
Msrc/backend/taler-merchant-donaukeyupdate.c | 7+++----
Msrc/backenddb/Makefile.am | 1+
Asrc/backenddb/pg_select_all_donau_instances.c | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/backenddb/pg_select_all_donau_instances.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/backenddb/pg_select_donau_instances.c | 98++++---------------------------------------------------------------------------
Msrc/backenddb/plugin_merchantdb_postgres.c | 3+++
Msrc/include/taler_merchantdb_plugin.h | 22++++++++++++++++++++--
7 files changed, 250 insertions(+), 99 deletions(-)

diff --git a/src/backend/taler-merchant-donaukeyupdate.c b/src/backend/taler-merchant-donaukeyupdate.c @@ -1069,10 +1069,9 @@ run (void *cls, { enum GNUNET_DB_QueryStatus qs; - qs = db_plugin->select_donau_instances (db_plugin->cls, - NULL, - &accept_donau, - NULL); + qs = db_plugin->select_all_donau_instances (db_plugin->cls, + &accept_donau, + NULL); if (qs < 0) { GNUNET_break (0); diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am @@ -228,6 +228,7 @@ if HAVE_DONAU libtaler_plugin_merchantdb_postgres_la_SOURCES += \ pg_insert_donau_instance.h pg_insert_donau_instance.c \ pg_select_donau_instances.h pg_select_donau_instances.c \ + pg_select_all_donau_instances.h pg_select_all_donau_instances.c \ pg_select_donau_instances_filtered.h pg_select_donau_instances_filtered.c \ pg_select_donau_instance_by_serial.h pg_select_donau_instance_by_serial.c \ pg_lookup_donau_keys.h pg_lookup_donau_keys.c \ diff --git a/src/backenddb/pg_select_all_donau_instances.c b/src/backenddb/pg_select_all_donau_instances.c @@ -0,0 +1,173 @@ +/* + This file is part of TALER + Copyright (C) 2024, 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_all_donau_instances.c + * @brief Implementation of the select_donau_instance function for Postgres + * @author Bohdan Potuzhnyi + * @author Vlada Svirsh + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_select_all_donau_instances.h" +#include "taler_merchant_donau.h" +#include "pg_helper.h" + +/** + * Context for select_donau_instances(). + */ +struct SelectDonauInstanceContext +{ + /** + * Function to call with the results. + */ + TALER_MERCHANTDB_DonauInstanceCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Did database result extraction fail? + */ + bool extract_failed; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results about donau instances. + * + * @param[in, out] cls of type `struct SelectDonauInstanceContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +select_donau_instance_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct SelectDonauInstanceContext *sdc = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + uint64_t donau_instance_serial; + char *donau_url; + char *charity_name; + struct DONAU_CharityPublicKeyP charity_pub_key; + uint64_t charity_id; + struct TALER_Amount charity_max_per_year; + struct TALER_Amount charity_receipts_to_date; + int64_t current_year; + json_t *donau_keys_json = NULL; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("donau_instances_serial", + &donau_instance_serial), + GNUNET_PQ_result_spec_string ("donau_url", + &donau_url), + GNUNET_PQ_result_spec_string ("charity_name", + &charity_name), + GNUNET_PQ_result_spec_auto_from_type ("charity_pub_key", + &charity_pub_key), + GNUNET_PQ_result_spec_uint64 ("charity_id", + &charity_id), + TALER_PQ_result_spec_amount_with_currency ("charity_max_per_year", + &charity_max_per_year), + TALER_PQ_result_spec_amount_with_currency ("charity_receipts_to_date", + &charity_receipts_to_date), + GNUNET_PQ_result_spec_int64 ("current_year", + &current_year), + GNUNET_PQ_result_spec_allow_null ( + TALER_PQ_result_spec_json ("keys_json", + &donau_keys_json), + NULL), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + sdc->extract_failed = true; + return; + } + sdc->cb (sdc->cb_cls, + donau_instance_serial, + donau_url, + charity_name, + &charity_pub_key, + charity_id, + &charity_max_per_year, + &charity_receipts_to_date, + current_year, + donau_keys_json); + GNUNET_PQ_cleanup_result (rs); + } +} + + +enum GNUNET_DB_QueryStatus +TMH_PG_select_all_donau_instances (void *cls, + TALER_MERCHANTDB_DonauInstanceCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct SelectDonauInstanceContext sdc = { + .cb = cb, + .cb_cls = cb_cls, + /* Can be overwritten by the select_donau_instance_cb */ + .extract_failed = false, + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + check_connection (pg); + PREPARE (pg, + "select_all_donau_instances", + "SELECT" + " di.donau_instances_serial" + ",di.donau_url" + ",di.charity_name" + ",mi.merchant_pub AS charity_pub_key" + ",di.charity_id" + ",di.charity_max_per_year" + ",di.charity_receipts_to_date" + ",di.current_year" + ",dk.keys_json" + " FROM merchant_donau_instances di" + " LEFT JOIN merchant_donau_keys dk" + " ON di.donau_url = dk.donau_url" + " JOIN merchant_instances mi" + " ON di.merchant_instance_serial = mi.merchant_serial"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "select_all_donau_instances", + params, + &select_donau_instance_cb, + &sdc); + + /* If there was an error inside select_donau_instance_cb, return a hard error. */ + if (sdc.extract_failed) + return GNUNET_DB_STATUS_HARD_ERROR; + + return qs; +} diff --git a/src/backenddb/pg_select_all_donau_instances.h b/src/backenddb/pg_select_all_donau_instances.h @@ -0,0 +1,45 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file backenddb/pg_select_all_donau_instances.h + * @brief implementation of the select_donau_instance function for Postgres + * @author Bohdan Potuzhnyi + * @author Vlada Svirsh + */ +#ifndef PG_SELECT_ALL_DONAU_INSTANCE_H +#define PG_SELECT_ALL_DONAU_INSTANCE_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "taler_merchantdb_plugin.h" +#include "taler_merchant_donau.h" + +/** + * Select all Donau instances from the database, regardless + * of instance or key status. + * + * @param cls the closure for the database context + * @param cb callback function to call with each result + * @param cb_cls closure for the callback + * @return status of the PG + */ +enum GNUNET_DB_QueryStatus +TMH_PG_select_all_donau_instances ( + void *cls, + TALER_MERCHANTDB_DonauInstanceCallback cb, + void *cb_cls); + +#endif diff --git a/src/backenddb/pg_select_donau_instances.c b/src/backenddb/pg_select_donau_instances.c @@ -122,77 +122,11 @@ select_donau_instance_cb (void *cls, } -/** - * Select all Donau instances (independent of instance) from the database. - * - * @param cls the closure for the database context - * @param cb callback function to call with each result - * @param cb_cls closure for the callback - * @return status of the PG - */ -static enum GNUNET_DB_QueryStatus -select_all_donau_instances (void *cls, - TALER_MERCHANTDB_DonauInstanceCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct SelectDonauInstanceContext sdc = { - .cb = cb, - .cb_cls = cb_cls, - /* Can be overwritten by the select_donau_instance_cb */ - .extract_failed = false, - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - PREPARE (pg, - "select_all_donau_instances", - "SELECT" - " di.donau_instances_serial" - ",di.donau_url" - ",di.charity_name" - ",mi.merchant_pub AS charity_pub_key" - ",di.charity_id" - ",di.charity_max_per_year" - ",di.charity_receipts_to_date" - ",di.current_year" - ",dk.keys_json" - " FROM merchant_donau_instances di" - " JOIN merchant_donau_keys dk" - " ON di.donau_url = dk.donau_url" - " JOIN merchant_instances mi" - " ON di.merchant_instance_serial = mi.merchant_serial"); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "select_all_donau_instances", - params, - &select_donau_instance_cb, - &sdc); - - /* If there was an error inside select_donau_instance_cb, return a hard error. */ - if (sdc.extract_failed) - return GNUNET_DB_STATUS_HARD_ERROR; - - return qs; -} - - -/** - * Select Donau instances from an instance @a id from the database. - * - * @param cls the closure for the database context - * @param id the ID of the merchant instance - * @param cb callback function to call with each result - * @param cb_cls closure for the callback - * @return status of the PG - */ -static enum GNUNET_DB_QueryStatus -select_donau_instances (void *cls, - const char *id, - TALER_MERCHANTDB_DonauInstanceCallback cb, - void *cb_cls) +enum GNUNET_DB_QueryStatus +TMH_PG_select_donau_instances (void *cls, + const char *id, + TALER_MERCHANTDB_DonauInstanceCallback cb, + void *cb_cls) { struct PostgresClosure *pg = cls; struct SelectDonauInstanceContext sdc = { @@ -238,25 +172,3 @@ select_donau_instances (void *cls, return qs; } - - -enum GNUNET_DB_QueryStatus -TMH_PG_select_donau_instances (void *cls, - const char *id, - TALER_MERCHANTDB_DonauInstanceCallback cb, - void *cb_cls) -{ - if (NULL == id) - { - return select_all_donau_instances (cls, - cb, - cb_cls); - } - else - { - return select_donau_instances (cls, - id, - cb, - cb_cls); - } -} diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c @@ -173,6 +173,7 @@ #include "donau/donau_service.h" #include "pg_insert_donau_instance.h" #include "pg_select_donau_instance_by_serial.h" +#include "pg_select_all_donau_instances.h" #include "pg_select_donau_instances.h" #include "pg_select_donau_instances_filtered.h" #include "pg_lookup_donau_keys.h" @@ -688,6 +689,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_insert_donau_instance; plugin->select_donau_instance_by_serial = &TMH_PG_select_donau_instance_by_serial; + plugin->select_all_donau_instances + = &TMH_PG_select_all_donau_instances; plugin->select_donau_instances = &TMH_PG_select_donau_instances; plugin->select_donau_instances_filtered diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h @@ -4314,10 +4314,12 @@ struct TALER_MERCHANTDB_Plugin uint64_t *charity_id); /** - * Select all Donau instances. + * Select all Donau instances. Note that this callback only + * returns Donau instances for which we have successfully + * retrieved /keys. * * @param cls closure - * @param id instance to restrict to, NULL for all + * @param id instance to restrict to * @param cb function to call on all Donau instances found * @param cb_cls closure for @a cb */ @@ -4328,6 +4330,22 @@ struct TALER_MERCHANTDB_Plugin TALER_MERCHANTDB_DonauInstanceCallback cb, void *cb_cls); + + /** + * Select all Donau instances, regardless of instance and also + * regardless of whether we got a /keys response. + * + * @param cls closure + * @param cb function to call on all Donau instances found + * @param cb_cls closure for @a cb + */ + enum GNUNET_DB_QueryStatus + (*select_all_donau_instances)( + void *cls, + TALER_MERCHANTDB_DonauInstanceCallback cb, + void *cb_cls); + + /** * Select all Donau instances, but only the donau_url * and charity_max_per_year.