/* This file is part of TALER Copyright (C) 2023 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 */ /** * @file backenddb/pg_lookup_reserve.c * @brief Implementation of the lookup_reserve function for Postgres * @author Iván Ávalos */ #include "platform.h" #include #include #include #include "pg_lookup_reserve.h" #include "pg_helper.h" /** * Closure for #lookup_reserve_rewards_cb(). */ struct LookupRewardsContext { /** * Postgres context. */ struct PostgresClosure *pg; /** * Array with information about rewards generated from this reserve. */ struct TALER_MERCHANTDB_RewardDetails *rewards; /** * Length of the @e rewards array. */ unsigned int rewards_length; /** * Set in case of errors. */ enum GNUNET_DB_QueryStatus qs; }; /** * Function to be called with the results of a SELECT statement * that has returned @a num_results results about accounts. * * @param[in,out] cls of type `struct LookupRewardsContext *` * @param result the postgres result * @param num_results the number of results in @a result */ static void lookup_reserve_rewards_cb (void *cls, PGresult *result, unsigned int num_results) { struct LookupRewardsContext *ltc = cls; GNUNET_array_grow (ltc->rewards, ltc->rewards_length, num_results); for (unsigned int i = 0; i < num_results; i++) { struct TALER_MERCHANTDB_RewardDetails *td = <c->rewards[i]; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_string ("justification", &td->reason), GNUNET_PQ_result_spec_auto_from_type ("reward_id", &td->reward_id), TALER_PQ_result_spec_amount_with_currency ("amount", &td->total_amount), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, i)) { GNUNET_break (0); ltc->qs = GNUNET_DB_STATUS_HARD_ERROR; return; } } } enum GNUNET_DB_QueryStatus TMH_PG_lookup_reserve (void *cls, const char *instance_id, const struct TALER_ReservePublicKeyP *reserve_pub, bool fetch_rewards, TALER_MERCHANTDB_ReserveDetailsCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct LookupRewardsContext ltc = { .pg = pg, .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT }; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (reserve_pub), GNUNET_PQ_query_param_end }; struct GNUNET_TIME_Timestamp creation_time; struct GNUNET_TIME_Timestamp expiration_time; struct TALER_Amount merchant_initial_balance; struct TALER_Amount exchange_initial_balance; struct TALER_Amount pickup_amount; struct TALER_Amount committed_amount; struct TALER_MasterPublicKeyP master_pub; bool active; char *exchange_url = NULL; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_timestamp ("creation_time", &creation_time), GNUNET_PQ_result_spec_timestamp ("expiration", &expiration_time), TALER_PQ_result_spec_amount_with_currency ("merchant_initial_balance", &merchant_initial_balance), TALER_PQ_result_spec_amount_with_currency ("exchange_initial_balance", &exchange_initial_balance), TALER_PQ_result_spec_amount_with_currency ("rewards_picked_up", &pickup_amount), TALER_PQ_result_spec_amount_with_currency ("rewards_committed", &committed_amount), GNUNET_PQ_result_spec_auto_from_type ("master_pub", &master_pub), GNUNET_PQ_result_spec_bool ("active", &active), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_string ("exchange_url", &exchange_url), NULL), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; check_connection (pg); PREPARE (pg, "lookup_reserve", "SELECT" " creation_time" ",expiration" ",merchant_initial_balance" ",exchange_initial_balance" ",rewards_committed" ",rewards_picked_up" ",reserve_priv IS NOT NULL AS active" ",exchange_url" ",master_pub" " FROM merchant_reward_reserves" " FULL OUTER JOIN merchant_reward_reserve_keys USING (reserve_serial)" " WHERE reserve_pub = $2" " AND merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "lookup_reserve", params, rs); if (qs < 0) return qs; if (! fetch_rewards) { cb (cb_cls, creation_time, expiration_time, &merchant_initial_balance, &exchange_initial_balance, &pickup_amount, &committed_amount, active, &master_pub, exchange_url, 0, NULL); GNUNET_PQ_cleanup_result (rs); return qs; } PREPARE (pg, "lookup_reserve_rewards", "SELECT" " justification" ",reward_id" ",amount" " FROM merchant_rewards" " WHERE reserve_serial =" " (SELECT reserve_serial" " FROM merchant_reward_reserves" " WHERE reserve_pub=$2" " AND merchant_serial =" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1))"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_reserve_rewards", params, &lookup_reserve_rewards_cb, <c); if (qs < 0) return qs; if (ltc.qs >= 0) { cb (cb_cls, creation_time, expiration_time, &merchant_initial_balance, &exchange_initial_balance, &pickup_amount, &committed_amount, active, &master_pub, exchange_url, ltc.rewards_length, ltc.rewards); } for (unsigned int i = 0; i