lookup_statistics_amount_by_bucket.c (7292B)
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/lookup_statistics_amount_by_bucket.c 18 * @brief Implementation of the lookup_statistics_amount_by_bucket function for Postgres 19 * @author Martin Schanzenbach 20 */ 21 #include "platform.h" 22 #include <taler/taler_pq_lib.h> 23 #include "merchant-database/lookup_statistics_amount_by_bucket.h" 24 #include "helper.h" 25 #include "merchantdb_lib.h" 26 27 28 /** 29 * Context used for TALER_MERCHANTDB_lookup_statistics_amount_by_bucket(). 30 */ 31 struct LookupAmountStatisticsContext 32 { 33 /** 34 * Function to call with the results. 35 */ 36 TALER_MERCHANTDB_AmountByBucketStatisticsCallback cb; 37 38 /** 39 * Closure for @a cb. 40 */ 41 void *cb_cls; 42 43 /** 44 * Did database result extraction fail? 45 */ 46 bool extract_failed; 47 48 /** 49 * Postgres context for array lookups 50 */ 51 struct TALER_MERCHANTDB_PostgresContext *pg; 52 }; 53 54 55 /** 56 * Function to be called with the results of a SELECT statement 57 * that has returned @a num_results results about token families. 58 * 59 * @param[in,out] cls of type `struct LookupAmountStatisticsContext *` 60 * @param result the postgres result 61 * @param num_results the number of results in @a result 62 */ 63 static void 64 lookup_statistics_amount_by_bucket_cb (void *cls, 65 PGresult *result, 66 unsigned int num_results) 67 { 68 struct LookupAmountStatisticsContext *tflc = cls; 69 struct TALER_Amount *amounts = NULL; 70 char *resp_range = NULL; 71 char *resp_desc = NULL; 72 uint64_t cur_bucket_start_epoch; 73 uint64_t cur_bucket_end_epoch; 74 uint64_t bmeta_id_current; 75 unsigned int amounts_len = 0; 76 77 for (unsigned int i = 0; i < num_results; i++) 78 { 79 struct TALER_Amount cumulative_amount; 80 char *description; 81 char *bucket_range; 82 uint64_t bmeta_id; 83 uint64_t bucket_start_epoch; 84 uint64_t bucket_end_epoch; 85 struct GNUNET_PQ_ResultSpec rs[] = { 86 GNUNET_PQ_result_spec_uint64 ("bmeta_serial_id", 87 &bmeta_id), 88 GNUNET_PQ_result_spec_string ("description", 89 &description), 90 GNUNET_PQ_result_spec_uint64 ("bucket_start", 91 &bucket_start_epoch), 92 GNUNET_PQ_result_spec_uint64 ("bucket_end", 93 &bucket_end_epoch), 94 GNUNET_PQ_result_spec_string ("bucket_range", 95 &bucket_range), 96 TALER_PQ_result_spec_amount_with_currency ("cumulative_amount", 97 &cumulative_amount), 98 GNUNET_PQ_result_spec_end 99 }; 100 101 if (GNUNET_OK != 102 GNUNET_PQ_extract_result (result, 103 rs, 104 i)) 105 { 106 GNUNET_break (0); 107 tflc->extract_failed = true; 108 return; 109 } 110 /* Call callback if the bucket changed */ 111 if ( (NULL != resp_desc) && 112 ( (bmeta_id != bmeta_id_current) || 113 (bucket_start_epoch != cur_bucket_start_epoch) || 114 (0 != strcasecmp (resp_range, 115 bucket_range)) ) ) 116 { 117 struct GNUNET_TIME_Timestamp bucket_start; 118 struct GNUNET_TIME_Timestamp bucket_end; 119 120 bucket_start = GNUNET_TIME_timestamp_from_s (cur_bucket_start_epoch); 121 bucket_end = GNUNET_TIME_timestamp_from_s (cur_bucket_end_epoch); 122 tflc->cb (tflc->cb_cls, 123 resp_desc, 124 bucket_start, 125 bucket_end, 126 resp_range, 127 amounts_len, 128 amounts); 129 GNUNET_free (resp_range); 130 GNUNET_free (resp_desc); 131 GNUNET_array_grow (amounts, 132 amounts_len, 133 0); 134 } 135 if (NULL == resp_desc) 136 { 137 cur_bucket_end_epoch = bucket_end_epoch; 138 cur_bucket_start_epoch = bucket_start_epoch; 139 resp_range = GNUNET_strdup (bucket_range); 140 resp_desc = GNUNET_strdup (description); 141 bmeta_id_current = bmeta_id; 142 } 143 GNUNET_array_append (amounts, 144 amounts_len, 145 cumulative_amount); 146 GNUNET_PQ_cleanup_result (rs); 147 } 148 if (0 != amounts_len) 149 { 150 struct GNUNET_TIME_Timestamp bucket_start; 151 struct GNUNET_TIME_Timestamp bucket_end; 152 153 bucket_start = GNUNET_TIME_timestamp_from_s (cur_bucket_start_epoch); 154 bucket_end = GNUNET_TIME_timestamp_from_s (cur_bucket_end_epoch); 155 tflc->cb (tflc->cb_cls, 156 resp_desc, 157 bucket_start, 158 bucket_end, 159 resp_range, 160 amounts_len, 161 amounts); 162 GNUNET_array_grow (amounts, 163 amounts_len, 164 0); 165 GNUNET_free (resp_range); 166 GNUNET_free (resp_desc); 167 } 168 } 169 170 171 enum GNUNET_DB_QueryStatus 172 TALER_MERCHANTDB_lookup_statistics_amount_by_bucket ( 173 struct TALER_MERCHANTDB_PostgresContext *pg, 174 const char *instance_id, 175 const char *slug, 176 TALER_MERCHANTDB_AmountByBucketStatisticsCallback cb, 177 void *cb_cls) 178 { 179 struct LookupAmountStatisticsContext context = { 180 .cb = cb, 181 .cb_cls = cb_cls, 182 /* Can be overwritten by the lookup_statistics_amount_by_bucket_cb */ 183 .extract_failed = false, 184 .pg = pg, 185 }; 186 struct GNUNET_PQ_QueryParam params[] = { 187 GNUNET_PQ_query_param_string (slug), 188 GNUNET_PQ_query_param_end 189 }; 190 enum GNUNET_DB_QueryStatus qs; 191 192 GNUNET_assert (NULL != pg->current_merchant_id); 193 GNUNET_assert (0 == strcmp (instance_id, 194 pg->current_merchant_id)); 195 check_connection (pg); 196 TMH_PQ_prepare_anon (pg, 197 "SELECT" 198 " bmeta_serial_id" 199 ",description" 200 ",bucket_start" 201 ",bucket_range::TEXT" 202 ",merchant_statistics_bucket_end(bucket_start, bucket_range) AS bucket_end" 203 ",(cumulative_value,cumulative_frac,curr)::merchant.taler_amount_currency AS cumulative_amount" 204 " FROM merchant_statistic_bucket_amount" 205 " JOIN merchant_statistic_bucket_meta" 206 " USING (bmeta_serial_id)" 207 " WHERE merchant_statistic_bucket_meta.slug=$1" 208 " AND merchant_statistic_bucket_meta.stype='amount'"); 209 qs = GNUNET_PQ_eval_prepared_multi_select ( 210 pg->conn, 211 "", 212 params, 213 &lookup_statistics_amount_by_bucket_cb, 214 &context); 215 /* If there was an error inside the cb, return a hard error. */ 216 if (context.extract_failed) 217 { 218 GNUNET_break (0); 219 return GNUNET_DB_STATUS_HARD_ERROR; 220 } 221 return qs; 222 }