Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file backenddb/pg_lookup_statistics_amount_by_interval.c
18 : * @brief Implementation of the lookup_statistics_amount_by_interval function for Postgres
19 : * @author Martin Schanzenbach
20 : */
21 : #include "platform.h"
22 : #include <taler/taler_error_codes.h>
23 : #include <taler/taler_dbevents.h>
24 : #include <taler/taler_pq_lib.h>
25 : #include "pg_lookup_statistics_amount_by_interval.h"
26 : #include "pg_helper.h"
27 : #include "taler_merchantdb_plugin.h"
28 :
29 :
30 : /**
31 : * Context used for TMH_PG_lookup_statistics_amount().
32 : */
33 : struct LookupAmountStatisticsContext
34 : {
35 : /**
36 : * Function to call with the results.
37 : */
38 : TALER_MERCHANTDB_AmountByIntervalStatisticsCallback cb;
39 :
40 : /**
41 : * Closure for @a cb.
42 : */
43 : void *cb_cls;
44 :
45 : /**
46 : * Did database result extraction fail?
47 : */
48 : bool extract_failed;
49 : };
50 :
51 :
52 : /**
53 : * Function to be called with the results of a SELECT statement
54 : * that has returned @a num_results results about token families.
55 : *
56 : * @param[in,out] cls of type `struct LookupTokenFamiliesContext *`
57 : * @param result the postgres result
58 : * @param num_results the number of results in @a result
59 : */
60 : static void
61 2 : lookup_statistics_amount_by_interval_cb (void *cls,
62 : PGresult *result,
63 : unsigned int num_results)
64 : {
65 2 : struct LookupAmountStatisticsContext *tflc = cls;
66 2 : struct TALER_Amount *amounts = NULL;
67 2 : char *resp_desc = NULL;
68 : uint64_t cur_interval_start_epoch;
69 : uint64_t bmeta_id_current;
70 2 : unsigned int amounts_len = 0;
71 :
72 2 : for (unsigned int i = 0; i < num_results; i++)
73 : {
74 : char *description;
75 : struct TALER_Amount cumulative_amount;
76 : uint64_t interval_start_epoch;
77 : uint64_t bmeta_id;
78 0 : struct GNUNET_PQ_ResultSpec rs[] = {
79 0 : GNUNET_PQ_result_spec_uint64 ("bmeta_serial_id",
80 : &bmeta_id),
81 0 : GNUNET_PQ_result_spec_string ("description",
82 : &description),
83 0 : GNUNET_PQ_result_spec_uint64 ("range",
84 : &interval_start_epoch),
85 0 : TALER_PQ_result_spec_amount_with_currency ("rvalue",
86 : &cumulative_amount),
87 : GNUNET_PQ_result_spec_end
88 : };
89 :
90 0 : if (GNUNET_OK !=
91 0 : GNUNET_PQ_extract_result (result,
92 : rs,
93 : i))
94 : {
95 0 : GNUNET_break (0);
96 0 : tflc->extract_failed = true;
97 0 : return;
98 : }
99 :
100 : /* Call callback if the bucket changed */
101 0 : if ( (NULL != resp_desc) &&
102 0 : ( (bmeta_id != bmeta_id_current) ||
103 0 : (interval_start_epoch != cur_interval_start_epoch)) )
104 : {
105 : struct GNUNET_TIME_Timestamp interval_start;
106 :
107 0 : interval_start = GNUNET_TIME_timestamp_from_s (cur_interval_start_epoch);
108 0 : tflc->cb (tflc->cb_cls,
109 : resp_desc,
110 : interval_start,
111 : amounts_len,
112 : amounts);
113 0 : GNUNET_array_grow (amounts,
114 : amounts_len,
115 : 0);
116 0 : GNUNET_free (resp_desc);
117 : }
118 0 : if (NULL == resp_desc)
119 : {
120 0 : cur_interval_start_epoch = interval_start_epoch;
121 0 : resp_desc = GNUNET_strdup (description);
122 0 : bmeta_id_current = bmeta_id;
123 : }
124 0 : GNUNET_array_append (amounts,
125 : amounts_len,
126 : cumulative_amount);
127 0 : GNUNET_PQ_cleanup_result (rs);
128 : }
129 2 : if (0 != amounts_len)
130 : {
131 : struct GNUNET_TIME_Timestamp interval_start;
132 :
133 0 : interval_start = GNUNET_TIME_timestamp_from_s (cur_interval_start_epoch);
134 0 : tflc->cb (tflc->cb_cls,
135 : resp_desc,
136 : interval_start,
137 : amounts_len,
138 : amounts);
139 0 : GNUNET_array_grow (amounts,
140 : amounts_len,
141 : 0);
142 0 : GNUNET_free (resp_desc);
143 : }
144 : }
145 :
146 :
147 : enum GNUNET_DB_QueryStatus
148 2 : TMH_PG_lookup_statistics_amount_by_interval (
149 : void *cls,
150 : const char *instance_id,
151 : const char *slug,
152 : TALER_MERCHANTDB_AmountByIntervalStatisticsCallback cb,
153 : void *cb_cls)
154 : {
155 2 : struct PostgresClosure *pg = cls;
156 2 : struct LookupAmountStatisticsContext context = {
157 : .cb = cb,
158 : .cb_cls = cb_cls,
159 : /* Can be overwritten by the lookup_statistics_amount_by_interval_cb */
160 : .extract_failed = false,
161 : };
162 2 : struct GNUNET_PQ_QueryParam params[] = {
163 2 : GNUNET_PQ_query_param_string (instance_id),
164 2 : GNUNET_PQ_query_param_string (slug),
165 : GNUNET_PQ_query_param_end
166 : };
167 : enum GNUNET_DB_QueryStatus qs;
168 :
169 2 : check_connection (pg);
170 2 : PREPARE (pg,
171 : "lookup_statistics_amount_by_interval",
172 : "SELECT *"
173 : " FROM merchant_statistic_interval_amount_get($1,$2)"
174 : " JOIN merchant_statistic_bucket_meta"
175 : " ON slug=$2");
176 2 : qs = GNUNET_PQ_eval_prepared_multi_select (
177 : pg->conn,
178 : "lookup_statistics_amount_by_interval",
179 : params,
180 : &lookup_statistics_amount_by_interval_cb,
181 : &context);
182 : /* If there was an error inside the cb, return a hard error. */
183 2 : if (context.extract_failed)
184 : {
185 0 : GNUNET_break (0);
186 0 : return GNUNET_DB_STATUS_HARD_ERROR;
187 : }
188 2 : return qs;
189 : }
|