Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2022, 2023, 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_orders.c
18 : * @brief Implementation of the lookup_orders function for Postgres
19 : * @author Iván Ávalos
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include <taler/taler_error_codes.h>
24 : #include <taler/taler_dbevents.h>
25 : #include <taler/taler_pq_lib.h>
26 : #include "pg_lookup_orders.h"
27 : #include "pg_helper.h"
28 :
29 : /**
30 : * Context used for TMH_PG_lookup_orders().
31 : */
32 : struct LookupOrdersContext
33 : {
34 : /**
35 : * Function to call with the results.
36 : */
37 : TALER_MERCHANTDB_OrdersCallback cb;
38 :
39 : /**
40 : * Closure for @a cb.
41 : */
42 : void *cb_cls;
43 :
44 : /**
45 : * Did database result extraction fail?
46 : */
47 : bool extract_failed;
48 : };
49 :
50 :
51 : /**
52 : * Function to be called with the results of a SELECT statement
53 : * that has returned @a num_results results about orders.
54 : *
55 : * @param[in,out] cls of type `struct LookupOrdersContext *`
56 : * @param result the postgres result
57 : * @param num_results the number of results in @a result
58 : */
59 : static void
60 14 : lookup_orders_cb (void *cls,
61 : PGresult *result,
62 : unsigned int num_results)
63 : {
64 14 : struct LookupOrdersContext *plc = cls;
65 :
66 27 : for (unsigned int i = 0; i < num_results; i++)
67 : {
68 : char *order_id;
69 : uint64_t order_serial;
70 : struct GNUNET_TIME_Timestamp ts;
71 13 : struct GNUNET_PQ_ResultSpec rs[] = {
72 13 : GNUNET_PQ_result_spec_string ("order_id",
73 : &order_id),
74 13 : GNUNET_PQ_result_spec_uint64 ("order_serial",
75 : &order_serial),
76 13 : GNUNET_PQ_result_spec_timestamp ("creation_time",
77 : &ts),
78 : GNUNET_PQ_result_spec_end
79 : };
80 :
81 13 : if (GNUNET_OK !=
82 13 : GNUNET_PQ_extract_result (result,
83 : rs,
84 : i))
85 : {
86 0 : GNUNET_break (0);
87 0 : plc->extract_failed = true;
88 0 : return;
89 : }
90 13 : plc->cb (plc->cb_cls,
91 : order_id,
92 : order_serial,
93 : ts);
94 13 : GNUNET_PQ_cleanup_result (rs);
95 : }
96 : }
97 :
98 :
99 : enum GNUNET_DB_QueryStatus
100 14 : TMH_PG_lookup_orders (void *cls,
101 : const char *instance_id,
102 : const struct TALER_MERCHANTDB_OrderFilter *of,
103 : TALER_MERCHANTDB_OrdersCallback cb,
104 : void *cb_cls)
105 : {
106 14 : struct PostgresClosure *pg = cls;
107 14 : struct LookupOrdersContext plc = {
108 : .cb = cb,
109 : .cb_cls = cb_cls
110 : };
111 14 : uint64_t limit = (of->delta > 0) ? of->delta : -of->delta;
112 14 : struct GNUNET_PQ_QueryParam params[] = {
113 : /* $1 */
114 14 : GNUNET_PQ_query_param_string (instance_id),
115 14 : GNUNET_PQ_query_param_uint64 (&limit),
116 14 : GNUNET_PQ_query_param_uint64 (&of->start_row),
117 14 : GNUNET_PQ_query_param_timestamp (&of->date),
118 14 : GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->paid)),
119 : /* $6 */
120 14 : GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->paid)),
121 14 : GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->refunded)),
122 14 : GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->refunded)),
123 14 : GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->wired)),
124 14 : GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->wired)),
125 : /* $11 */
126 14 : GNUNET_PQ_query_param_bool (NULL == of->session_id),
127 14 : NULL == of->session_id
128 14 : ? GNUNET_PQ_query_param_null ()
129 14 : : GNUNET_PQ_query_param_string (of->session_id),
130 14 : GNUNET_PQ_query_param_bool (NULL == of->fulfillment_url),
131 14 : NULL == of->fulfillment_url
132 14 : ? GNUNET_PQ_query_param_null ()
133 14 : : GNUNET_PQ_query_param_string (of->fulfillment_url),
134 14 : NULL == of->summary_filter
135 14 : ? GNUNET_PQ_query_param_null ()
136 14 : : GNUNET_PQ_query_param_string (of->summary_filter),
137 : GNUNET_PQ_query_param_end
138 : };
139 : enum GNUNET_DB_QueryStatus qs;
140 : char stmt[128];
141 :
142 14 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
143 : "Looking up orders, using filter paid: %d, refunded: %d, wired: %d\n",
144 : of->paid,
145 : of->refunded,
146 : of->wired);
147 14 : GNUNET_snprintf (stmt,
148 : sizeof (stmt),
149 : "lookup_orders_%s",
150 14 : (of->delta > 0) ? "inc" : "dec");
151 14 : PREPARE (pg,
152 : "lookup_orders_dec",
153 : "(SELECT"
154 : " order_id"
155 : ",order_serial"
156 : ",creation_time"
157 : " FROM merchant_orders"
158 : " WHERE merchant_orders.merchant_serial="
159 : " (SELECT merchant_serial "
160 : " FROM merchant_instances"
161 : " WHERE merchant_id=$1)"
162 : " AND"
163 : " order_serial < $3"
164 : " AND"
165 : " creation_time < $4"
166 : " AND ($5 OR "
167 : " NOT CAST($6 AS BOOL))" /* unclaimed orders are never paid */
168 : " AND ($7 OR "
169 : " NOT CAST($8 AS BOOL))"/* unclaimed orders are never refunded */
170 : " AND ($9 OR "
171 : " NOT CAST($10 AS BOOL))" /* unclaimed orders are never wired */
172 : " AND"
173 : " order_serial NOT IN"
174 : " (SELECT order_serial"
175 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
176 : " AND ($11 OR "
177 : " ($12 = session_id))"
178 : " AND ($13 OR "
179 : " ($14 = fulfillment_url))"
180 : " AND ( ($15::TEXT IS NULL) OR "
181 : " (LOWER(contract_terms ->> 'summary') LIKE LOWER($15)) )"
182 : " ORDER BY order_serial DESC"
183 : " LIMIT $2)"
184 : "UNION " /* union ensures elements are distinct! */
185 : "(SELECT"
186 : " order_id"
187 : ",order_serial"
188 : ",creation_time"
189 : " FROM merchant_contract_terms"
190 : " WHERE merchant_contract_terms.merchant_serial="
191 : " (SELECT merchant_serial "
192 : " FROM merchant_instances"
193 : " WHERE merchant_id=$1)"
194 : " AND"
195 : " order_serial < $3"
196 : " AND"
197 : " creation_time < $4"
198 : " AND ($5 OR "
199 : " (CAST($6 AS BOOL) = paid))"
200 : " AND ($7 OR "
201 : " (CAST($8 AS BOOL) = (order_serial IN"
202 : " (SELECT order_serial "
203 : " FROM merchant_refunds))))"
204 : " AND ($9 OR"
205 : " (CAST($10 AS BOOL) = wired))"
206 : " AND ($11 OR "
207 : " ($12 = session_id))"
208 : " AND ($13 OR "
209 : " ($14 = fulfillment_url))"
210 : " AND ( ($15::TEXT IS NULL) OR "
211 : " (LOWER(contract_terms ->> 'summary') LIKE LOWER($15)) )"
212 : " ORDER BY order_serial DESC"
213 : " LIMIT $2)"
214 : " ORDER BY order_serial DESC"
215 : " LIMIT $2");
216 :
217 14 : PREPARE (pg,
218 : "lookup_orders_inc",
219 : "(SELECT"
220 : " order_id"
221 : ",order_serial"
222 : ",creation_time"
223 : " FROM merchant_orders"
224 : " WHERE merchant_orders.merchant_serial="
225 : " (SELECT merchant_serial "
226 : " FROM merchant_instances"
227 : " WHERE merchant_id=$1)"
228 : " AND"
229 : " order_serial > $3"
230 : " AND"
231 : " creation_time > $4"
232 : " AND ($5 OR "
233 : " NOT CAST($6 AS BOOL))" /* unclaimed orders are never paid */
234 : " AND ($7 OR "
235 : " NOT CAST($8 AS BOOL))"/* unclaimed orders are never refunded */
236 : " AND ($9 OR "
237 : " NOT CAST($10 AS BOOL))" /* unclaimed orders are never wired */
238 : " AND"
239 : " (order_serial NOT IN"
240 : " (SELECT order_serial"
241 : " FROM merchant_contract_terms))" /* only select unclaimed orders */
242 : " AND ($11 OR "
243 : " ($12 = session_id))"
244 : " AND ($13 OR "
245 : " ($14 = fulfillment_url))"
246 : " AND ( ($15::TEXT IS NULL) OR "
247 : " (LOWER(contract_terms ->> 'summary') LIKE LOWER($15)) )"
248 : " ORDER BY order_serial ASC"
249 : " LIMIT $2)"
250 : "UNION " /* union ensures elements are distinct! */
251 : "(SELECT"
252 : " order_id"
253 : ",order_serial"
254 : ",creation_time"
255 : " FROM merchant_contract_terms"
256 : " WHERE merchant_contract_terms.merchant_serial="
257 : " (SELECT merchant_serial "
258 : " FROM merchant_instances"
259 : " WHERE merchant_id=$1)"
260 : " AND"
261 : " order_serial > $3"
262 : " AND"
263 : " creation_time > $4"
264 : " AND ($5 OR "
265 : " (CAST($6 AS BOOL) = paid))"
266 : " AND ($7 OR "
267 : " (CAST($8 AS BOOL) = (order_serial IN"
268 : " (SELECT order_serial "
269 : " FROM merchant_refunds))))"
270 : " AND ($9 OR"
271 : " (CAST($10 AS BOOL) = wired))"
272 : " AND ($11 OR "
273 : " ($12 = session_id))"
274 : " AND ($13 OR "
275 : " ($14 = fulfillment_url))"
276 : " AND ( ($15::TEXT IS NULL) OR "
277 : " (LOWER(contract_terms ->> 'summary') LIKE LOWER($15)) )"
278 : " ORDER BY order_serial ASC"
279 : " LIMIT $2)"
280 : " ORDER BY order_serial ASC"
281 : " LIMIT $2");
282 :
283 14 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
284 : stmt,
285 : params,
286 : &lookup_orders_cb,
287 : &plc);
288 14 : if (plc.extract_failed)
289 0 : return GNUNET_DB_STATUS_HARD_ERROR;
290 14 : return qs;
291 : }
|