iterate_aml_history.c (6020B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024 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 exchangedb/iterate_aml_history.c 18 * @brief Implementation of the iterate_aml_history function for Postgres 19 * @author Christian Grothoff 20 */ 21 #include "taler/taler_pq_lib.h" 22 #include "exchange-database/iterate_aml_history.h" 23 #include "helper.h" 24 25 26 /** 27 * Hard upper bound on the number of records returned by a single 28 * call, regardless of the limit requested by the client. 29 */ 30 #define MAX_RECORDS 50000 31 32 33 /** 34 * Closure for callbacks called from #TALER_EXCHANGEDB_iterate_aml_history() 35 */ 36 struct AmlHistoryContext 37 { 38 39 /** 40 * Function to call on each result. 41 */ 42 TALER_EXCHANGEDB_AmlHistoryCallback cb; 43 44 /** 45 * Closure for @e cb. 46 */ 47 void *cb_cls; 48 49 /** 50 * Plugin context. 51 */ 52 struct TALER_EXCHANGEDB_PostgresContext *pg; 53 54 /** 55 * Set to 'true' if the transaction failed. 56 */ 57 bool failed; 58 59 }; 60 61 62 /** 63 * Function to be called with the results of a SELECT statement 64 * that has returned @a num_results results. 65 * 66 * @param cls closure of type `struct AmlHistoryContext` 67 * @param result the postgres result 68 * @param num_results the number of results in @a result 69 */ 70 static void 71 handle_aml_entry (void *cls, 72 PGresult *result, 73 unsigned int num_results) 74 { 75 struct AmlHistoryContext *ahc = cls; 76 77 for (unsigned int i = 0; i < num_results; i++) 78 { 79 uint64_t outcome_serial_id; 80 struct GNUNET_TIME_Timestamp decision_time; 81 char *justification; 82 struct TALER_AmlOfficerPublicKeyP decider_pub; 83 json_t *jproperties = NULL; 84 json_t *jnew_rules = NULL; 85 bool to_investigate; 86 bool is_active; 87 struct GNUNET_PQ_ResultSpec rs[] = { 88 GNUNET_PQ_result_spec_uint64 ("outcome_serial_id", 89 &outcome_serial_id), 90 GNUNET_PQ_result_spec_timestamp ("decision_time", 91 &decision_time), 92 GNUNET_PQ_result_spec_string ("justification", 93 &justification), 94 GNUNET_PQ_result_spec_auto_from_type ("decider_pub", 95 &decider_pub), 96 GNUNET_PQ_result_spec_allow_null ( 97 TALER_PQ_result_spec_json ("jproperties", 98 &jproperties), 99 NULL), 100 TALER_PQ_result_spec_json ("jnew_rules", 101 &jnew_rules), 102 GNUNET_PQ_result_spec_bool ("to_investigate", 103 &to_investigate), 104 GNUNET_PQ_result_spec_bool ("is_active", 105 &is_active), 106 GNUNET_PQ_result_spec_end 107 }; 108 109 if (GNUNET_OK != 110 GNUNET_PQ_extract_result (result, 111 rs, 112 i)) 113 { 114 GNUNET_break (0); 115 ahc->failed = true; 116 return; 117 } 118 ahc->cb (ahc->cb_cls, 119 outcome_serial_id, 120 decision_time, 121 justification, 122 &decider_pub, 123 jproperties, 124 jnew_rules, 125 to_investigate, 126 is_active); 127 GNUNET_PQ_cleanup_result (rs); 128 } 129 } 130 131 132 enum GNUNET_DB_QueryStatus 133 TALER_EXCHANGEDB_iterate_aml_history ( 134 struct TALER_EXCHANGEDB_PostgresContext *pg, 135 const struct TALER_NormalizedPaytoHashP *h_payto, 136 uint64_t offset, 137 int64_t limit, 138 TALER_EXCHANGEDB_AmlHistoryCallback cb, 139 void *cb_cls) 140 { 141 struct AmlHistoryContext ahc = { 142 .pg = pg, 143 .cb = cb, 144 .cb_cls = cb_cls 145 }; 146 uint64_t ulimit = GNUNET_MIN ((uint64_t) MAX_RECORDS, 147 TALER_EXCHANGEDB_abs_limit (limit)); 148 struct GNUNET_PQ_QueryParam params[] = { 149 GNUNET_PQ_query_param_auto_from_type (h_payto), 150 GNUNET_PQ_query_param_uint64 (&offset), 151 GNUNET_PQ_query_param_uint64 (&ulimit), 152 GNUNET_PQ_query_param_end 153 }; 154 enum GNUNET_DB_QueryStatus qs; 155 156 PREPARE (pg, 157 "iterate_aml_history_desc", 158 "SELECT" 159 " lo.decision_time" 160 ",lo.outcome_serial_id" 161 ",ah.justification" 162 ",ah.decider_pub" 163 ",lo.jproperties::TEXT" 164 ",lo.jnew_rules::TEXT" 165 ",lo.to_investigate" 166 ",lo.is_active" 167 " FROM aml_history ah" 168 " JOIN legitimization_outcomes lo" 169 " USING (outcome_serial_id)" 170 " WHERE ah.h_payto=$1" 171 " AND lo.outcome_serial_id < $2" 172 " ORDER BY outcome_serial_id DESC" 173 " LIMIT $3;"); 174 PREPARE (pg, 175 "iterate_aml_history_asc", 176 "SELECT" 177 " lo.decision_time" 178 ",lo.outcome_serial_id" 179 ",ah.justification" 180 ",ah.decider_pub" 181 ",lo.jproperties::TEXT" 182 ",lo.jnew_rules::TEXT" 183 ",lo.to_investigate" 184 ",lo.is_active" 185 " FROM aml_history ah" 186 " JOIN legitimization_outcomes lo" 187 " USING (outcome_serial_id)" 188 " WHERE ah.h_payto=$1" 189 " AND lo.outcome_serial_id > $2" 190 " ORDER BY outcome_serial_id ASC" 191 " LIMIT $3;"); 192 qs = GNUNET_PQ_eval_prepared_multi_select ( 193 pg->conn, 194 (limit < 0) 195 ? "iterate_aml_history_desc" 196 : "iterate_aml_history_asc", 197 params, 198 &handle_aml_entry, 199 &ahc); 200 if (qs <= 0) 201 return qs; 202 if (ahc.failed) 203 { 204 GNUNET_break (0); 205 return GNUNET_DB_STATUS_HARD_ERROR; 206 } 207 return qs; 208 }