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 src/backenddb/iterate_expected_transfers.c
18 : * @brief Implementation of the iterate_expected_transfers function for Postgres
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <taler/taler_pq_lib.h>
23 : #include "merchant-database/iterate_expected_transfers.h"
24 : #include "helper.h"
25 : #include <microhttpd.h> /* for HTTP status codes */
26 :
27 :
28 : /**
29 : * Hard upper bound on the number of records returned by a single
30 : * call, regardless of the limit requested by the client.
31 : */
32 : #define MAX_RECORDS 50000
33 :
34 :
35 : /**
36 : * Closure for #lookup_expected_transfers_cb().
37 : */
38 : struct LookupExpectedTransfersContext
39 : {
40 : /**
41 : * Function to call on results.
42 : */
43 : TALER_MERCHANTDB_IncomingCallback cb;
44 :
45 : /**
46 : * Closure for @e cb.
47 : */
48 : void *cb_cls;
49 :
50 : /**
51 : * Postgres context.
52 : */
53 : struct TALER_MERCHANTDB_PostgresContext *pg;
54 :
55 : /**
56 : * Transaction status (set).
57 : */
58 : enum GNUNET_DB_QueryStatus qs;
59 :
60 : };
61 :
62 :
63 : /**
64 : * Function to be called with the results of a SELECT statement
65 : * that has returned @a num_results results.
66 : *
67 : * @param cls of type `struct LookupExpectedTransfersContext *`
68 : * @param result the postgres result
69 : * @param num_results the number of results in @a result
70 : */
71 : static void
72 0 : lookup_expected_transfers_cb (void *cls,
73 : PGresult *result,
74 : unsigned int num_results)
75 : {
76 0 : struct LookupExpectedTransfersContext *ltc = cls;
77 :
78 0 : for (unsigned int i = 0; i<num_results; i++)
79 : {
80 : struct TALER_Amount expected_credit_amount;
81 : struct TALER_WireTransferIdentifierRawP wtid;
82 : struct TALER_FullPayto payto_uri;
83 : char *exchange_url;
84 : uint64_t expected_transfer_serial_id;
85 0 : struct GNUNET_TIME_Timestamp execution_time
86 : = GNUNET_TIME_UNIT_ZERO_TS;
87 : bool confirmed;
88 : bool validated;
89 : bool no_amount;
90 0 : char *last_detail = NULL;
91 0 : uint32_t last_http_status = 0;
92 0 : uint32_t last_ec = TALER_EC_NONE;
93 0 : struct GNUNET_PQ_ResultSpec rs[] = {
94 0 : GNUNET_PQ_result_spec_allow_null (
95 : TALER_PQ_result_spec_amount_with_currency ("expected_credit_amount",
96 : &expected_credit_amount),
97 : &no_amount),
98 0 : GNUNET_PQ_result_spec_auto_from_type ("wtid",
99 : &wtid),
100 0 : GNUNET_PQ_result_spec_string ("payto_uri",
101 : &payto_uri.full_payto),
102 0 : GNUNET_PQ_result_spec_string ("exchange_url",
103 : &exchange_url),
104 0 : GNUNET_PQ_result_spec_uint64 ("expected_credit_serial",
105 : &expected_transfer_serial_id),
106 0 : GNUNET_PQ_result_spec_allow_null (
107 : GNUNET_PQ_result_spec_timestamp ("execution_time",
108 : &execution_time),
109 : NULL),
110 0 : GNUNET_PQ_result_spec_bool ("confirmed",
111 : &confirmed),
112 0 : GNUNET_PQ_result_spec_allow_null (
113 : GNUNET_PQ_result_spec_uint32 ("last_http_status",
114 : &last_http_status),
115 : NULL),
116 0 : GNUNET_PQ_result_spec_allow_null (
117 : GNUNET_PQ_result_spec_uint32 ("last_ec",
118 : &last_ec),
119 : NULL),
120 0 : GNUNET_PQ_result_spec_allow_null (
121 : GNUNET_PQ_result_spec_string ("last_detail",
122 : &last_detail),
123 : NULL),
124 : GNUNET_PQ_result_spec_end
125 : };
126 :
127 0 : if (GNUNET_OK !=
128 0 : GNUNET_PQ_extract_result (result,
129 : rs,
130 : i))
131 : {
132 0 : GNUNET_break (0);
133 0 : ltc->qs = GNUNET_DB_STATUS_HARD_ERROR;
134 0 : return;
135 : }
136 0 : validated = ( (MHD_HTTP_OK == last_http_status) &&
137 0 : (TALER_EC_NONE == last_ec) );
138 0 : ltc->cb (ltc->cb_cls,
139 : no_amount
140 : ? NULL
141 : : &expected_credit_amount,
142 : &wtid,
143 : payto_uri,
144 : exchange_url,
145 : expected_transfer_serial_id,
146 : execution_time,
147 : confirmed,
148 : validated,
149 : last_http_status,
150 : last_ec,
151 : last_detail);
152 0 : GNUNET_PQ_cleanup_result (rs);
153 : }
154 0 : ltc->qs = num_results;
155 : }
156 :
157 :
158 : enum GNUNET_DB_QueryStatus
159 0 : TALER_MERCHANTDB_iterate_expected_transfers (
160 : struct TALER_MERCHANTDB_PostgresContext *pg,
161 : const char *instance_id,
162 : struct TALER_FullPayto payto_uri,
163 : struct GNUNET_TIME_Timestamp before,
164 : struct GNUNET_TIME_Timestamp after,
165 : int64_t limit,
166 : uint64_t offset,
167 : enum TALER_EXCHANGE_YesNoAll confirmed,
168 : enum TALER_EXCHANGE_YesNoAll verified,
169 : TALER_MERCHANTDB_IncomingCallback cb,
170 : void *cb_cls)
171 : {
172 0 : uint64_t plimit = GNUNET_MIN ((uint64_t) MAX_RECORDS,
173 : TALER_MERCHANTDB_abs_limit (limit));
174 0 : bool by_time = ( (! GNUNET_TIME_absolute_is_never (before.abs_time)) ||
175 0 : (! GNUNET_TIME_absolute_is_zero (after.abs_time)) );
176 0 : struct LookupExpectedTransfersContext ltc = {
177 : .cb = cb,
178 : .cb_cls = cb_cls,
179 : .pg = pg
180 : };
181 0 : struct GNUNET_PQ_QueryParam params[] = {
182 0 : GNUNET_PQ_query_param_timestamp (&before),
183 0 : GNUNET_PQ_query_param_timestamp (&after),
184 0 : GNUNET_PQ_query_param_uint64 (&offset),
185 0 : GNUNET_PQ_query_param_uint64 (&plimit),
186 0 : NULL == payto_uri.full_payto
187 0 : ? GNUNET_PQ_query_param_null () /* NULL: do not filter by payto URI */
188 0 : : GNUNET_PQ_query_param_string (payto_uri.full_payto),
189 0 : GNUNET_PQ_query_param_bool (! by_time), /* $6: filter by time? */
190 0 : GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_ALL == confirmed), /* filter by confirmed? */
191 0 : GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_YES == confirmed),
192 0 : GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_ALL == verified), /* filter by verified? */
193 0 : GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_YES == verified),
194 :
195 : GNUNET_PQ_query_param_end
196 : };
197 : enum GNUNET_DB_QueryStatus qs;
198 :
199 0 : GNUNET_assert (NULL != pg->current_merchant_id);
200 0 : GNUNET_assert (0 == strcmp (instance_id,
201 : pg->current_merchant_id));
202 0 : if (limit > 0)
203 : {
204 0 : TMH_PQ_prepare_anon (pg,
205 : "SELECT"
206 : " met.expected_credit_amount"
207 : ",met.wtid"
208 : ",mac.payto_uri"
209 : ",met.exchange_url"
210 : ",met.expected_credit_serial"
211 : ",mts.execution_time"
212 : ",met.confirmed"
213 : ",met.last_http_status"
214 : ",met.last_ec"
215 : ",met.last_detail"
216 : " FROM merchant_expected_transfers met"
217 : " JOIN merchant_accounts mac"
218 : " USING (account_serial)"
219 : " LEFT JOIN merchant_transfer_signatures mts"
220 : " USING (expected_credit_serial)"
221 : " WHERE ( $6 OR "
222 : " (mts.execution_time IS NOT NULL AND"
223 : " mts.execution_time < $1 AND"
224 : " mts.execution_time >= $2) )"
225 : " AND ( (CAST($5 AS TEXT) IS NULL) OR "
226 : " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')"
227 : " =REGEXP_REPLACE($5,'\\?.*','')) )"
228 : " AND ( $7 OR "
229 : " (met.confirmed = $8) )"
230 : " AND ( $9 OR "
231 : " ($10 = (200=met.last_http_status) AND"
232 : " (0=met.last_ec) ) )"
233 : " AND (met.expected_credit_serial > $3)"
234 : " ORDER BY met.expected_credit_serial ASC"
235 : " LIMIT $4");
236 : }
237 : else
238 : {
239 0 : TMH_PQ_prepare_anon (pg,
240 : "SELECT"
241 : " met.expected_credit_amount"
242 : ",met.wtid"
243 : ",mac.payto_uri"
244 : ",met.exchange_url"
245 : ",met.expected_credit_serial"
246 : ",mts.execution_time"
247 : ",met.confirmed"
248 : ",met.last_http_status"
249 : ",met.last_ec"
250 : ",met.last_detail"
251 : " FROM merchant_expected_transfers met"
252 : " JOIN merchant_accounts mac"
253 : " USING (account_serial)"
254 : " LEFT JOIN merchant_transfer_signatures mts"
255 : " USING (expected_credit_serial)"
256 : " WHERE ( $6 OR "
257 : " (mts.execution_time IS NOT NULL AND"
258 : " mts.execution_time < $1 AND"
259 : " mts.execution_time >= $2) )"
260 : " AND ( (CAST($5 AS TEXT) IS NULL) OR "
261 : " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')"
262 : " =REGEXP_REPLACE($5,'\\?.*','')) )"
263 : " AND ( $7 OR "
264 : " (met.confirmed = $8) )"
265 : " AND ( $9 OR "
266 : " ($10 = (200=met.last_http_status) AND"
267 : " (0=met.last_ec) ) )"
268 : " AND (met.expected_credit_serial < $3)"
269 : " ORDER BY met.expected_credit_serial DESC"
270 : " LIMIT $4");
271 : }
272 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
273 : pg->conn,
274 : "",
275 : params,
276 : &lookup_expected_transfers_cb,
277 : <c);
278 0 : if (0 >= qs)
279 0 : return qs;
280 0 : return ltc.qs;
281 : }
|