merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

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 }