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