merchant

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

pg_lookup_statistics_amount_by_bucket.c (7258B)


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