LCOV - code coverage report
Current view: top level - pq - pq_result_helper.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 92 117 78.6 %
Date: 2021-08-30 06:43:37 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014, 2015, 2016 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 pq/pq_result_helper.c
      18             :  * @brief functions to initialize parameter arrays
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include <gnunet/gnunet_util_lib.h>
      23             : #include "taler_pq_lib.h"
      24             : 
      25             : 
      26             : /**
      27             :  * Extract a currency amount from a query result according to the
      28             :  * given specification.
      29             :  *
      30             :  * @param result the result to extract the amount from
      31             :  * @param row which row of the result to extract the amount from (needed as results can have multiple rows)
      32             :  * @param currency currency to use for @a r_amount_nbo
      33             :  * @param val_name name of the column with the amount's "value", must include the substring "_val".
      34             :  * @param frac_name name of the column with the amount's "fractional" value, must include the substring "_frac".
      35             :  * @param[out] r_amount_nbo where to store the amount, in network byte order
      36             :  * @return
      37             :  *   #GNUNET_YES if all results could be extracted
      38             :  *   #GNUNET_NO if at least one result was NULL
      39             :  *   #GNUNET_SYSERR if a result was invalid (non-existing field)
      40             :  */
      41             : static enum GNUNET_GenericReturnValue
      42      381451 : extract_amount_nbo_helper (PGresult *result,
      43             :                            int row,
      44             :                            const char *currency,
      45             :                            const char *val_name,
      46             :                            const char *frac_name,
      47             :                            struct TALER_AmountNBO *r_amount_nbo)
      48             : {
      49             :   int val_num;
      50             :   int frac_num;
      51             :   int len;
      52             : 
      53             :   /* These checks are simply to check that clients obey by our naming
      54             :      conventions, and not for any functional reason */
      55      381451 :   GNUNET_assert (NULL !=
      56             :                  strstr (val_name,
      57             :                          "_val"));
      58      381451 :   GNUNET_assert (NULL !=
      59             :                  strstr (frac_name,
      60             :                          "_frac"));
      61             :   /* Set return value to invalid in case we don't finish */
      62      381451 :   memset (r_amount_nbo,
      63             :           0,
      64             :           sizeof (struct TALER_AmountNBO));
      65      381451 :   val_num = PQfnumber (result,
      66             :                        val_name);
      67      381451 :   frac_num = PQfnumber (result,
      68             :                         frac_name);
      69      381451 :   if (val_num < 0)
      70             :   {
      71           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      72             :                 "Field `%s' does not exist in result\n",
      73             :                 val_name);
      74           0 :     return GNUNET_SYSERR;
      75             :   }
      76      381451 :   if (frac_num < 0)
      77             :   {
      78           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      79             :                 "Field `%s' does not exist in result\n",
      80             :                 frac_name);
      81           0 :     return GNUNET_SYSERR;
      82             :   }
      83      381451 :   if ( (PQgetisnull (result,
      84             :                      row,
      85      381451 :                      val_num)) ||
      86      381451 :        (PQgetisnull (result,
      87             :                      row,
      88             :                      frac_num)) )
      89             :   {
      90           0 :     return GNUNET_NO;
      91             :   }
      92             :   /* Note that Postgres stores value in NBO internally,
      93             :      so no conversion needed in this case */
      94      381451 :   r_amount_nbo->value = *(uint64_t *) PQgetvalue (result,
      95             :                                                   row,
      96             :                                                   val_num);
      97      381451 :   r_amount_nbo->fraction = *(uint32_t *) PQgetvalue (result,
      98             :                                                      row,
      99             :                                                      frac_num);
     100      381451 :   len = GNUNET_MIN (TALER_CURRENCY_LEN - 1,
     101             :                     strlen (currency));
     102      381451 :   memcpy (r_amount_nbo->currency,
     103             :           currency,
     104             :           len);
     105      381451 :   return GNUNET_OK;
     106             : }
     107             : 
     108             : 
     109             : /**
     110             :  * Extract data from a Postgres database @a result at row @a row.
     111             :  *
     112             :  * @param cls closure, a `const char *` giving the currency
     113             :  * @param result where to extract data from
     114             :  * @param row row to extract data from
     115             :  * @param fname name (or prefix) of the fields to extract from
     116             :  * @param[in,out] dst_size where to store size of result, may be NULL
     117             :  * @param[out] dst where to store the result
     118             :  * @return
     119             :  *   #GNUNET_YES if all results could be extracted
     120             :  *   #GNUNET_NO if at least one result was NULL
     121             :  *   #GNUNET_SYSERR if a result was invalid (non-existing field)
     122             :  */
     123             : static enum GNUNET_GenericReturnValue
     124      371841 : extract_amount_nbo (void *cls,
     125             :                     PGresult *result,
     126             :                     int row,
     127             :                     const char *fname,
     128             :                     size_t *dst_size,
     129             :                     void *dst)
     130             : {
     131      371841 :   const char *currency = cls;
     132             :   char *val_name;
     133             :   char *frac_name;
     134             :   enum GNUNET_GenericReturnValue ret;
     135             : 
     136      371841 :   if (sizeof (struct TALER_AmountNBO) != *dst_size)
     137             :   {
     138           0 :     GNUNET_break (0);
     139           0 :     return GNUNET_SYSERR;
     140             :   }
     141      371841 :   GNUNET_asprintf (&val_name,
     142             :                    "%s_val",
     143             :                    fname);
     144      371841 :   GNUNET_asprintf (&frac_name,
     145             :                    "%s_frac",
     146             :                    fname);
     147      371841 :   ret = extract_amount_nbo_helper (result,
     148             :                                    row,
     149             :                                    currency,
     150             :                                    val_name,
     151             :                                    frac_name,
     152             :                                    dst);
     153      371841 :   GNUNET_free (val_name);
     154      371841 :   GNUNET_free (frac_name);
     155      371841 :   return ret;
     156             : }
     157             : 
     158             : 
     159             : /**
     160             :  * Currency amount expected.
     161             :  *
     162             :  * @param name name of the field in the table
     163             :  * @param currency the currency to use for @a amount
     164             :  * @param[out] amount where to store the result
     165             :  * @return array entry for the result specification to use
     166             :  */
     167             : struct GNUNET_PQ_ResultSpec
     168      371846 : TALER_PQ_result_spec_amount_nbo (const char *name,
     169             :                                  const char *currency,
     170             :                                  struct TALER_AmountNBO *amount)
     171             : {
     172      371846 :   struct GNUNET_PQ_ResultSpec res = {
     173             :     .conv = &extract_amount_nbo,
     174             :     .cls = (void *) currency,
     175             :     .dst = (void *) amount,
     176             :     .dst_size = sizeof (*amount),
     177             :     .fname = name
     178             :   };
     179             : 
     180      371846 :   return res;
     181             : }
     182             : 
     183             : 
     184             : /**
     185             :  * Extract data from a Postgres database @a result at row @a row.
     186             :  *
     187             :  * @param cls closure, a `const char *` giving the currency
     188             :  * @param result where to extract data from
     189             :  * @param row row to extract data from
     190             :  * @param fname name (or prefix) of the fields to extract from
     191             :  * @param[in,out] dst_size where to store size of result, may be NULL
     192             :  * @param[out] dst where to store the result
     193             :  * @return
     194             :  *   #GNUNET_YES if all results could be extracted
     195             :  *   #GNUNET_NO if at least one result was NULL
     196             :  *   #GNUNET_SYSERR if a result was invalid (non-existing field)
     197             :  */
     198             : static enum GNUNET_GenericReturnValue
     199        9610 : extract_amount (void *cls,
     200             :                 PGresult *result,
     201             :                 int row,
     202             :                 const char *fname,
     203             :                 size_t *dst_size,
     204             :                 void *dst)
     205             : {
     206        9610 :   const char *currency = cls;
     207        9610 :   struct TALER_Amount *r_amount = dst;
     208             :   char *val_name;
     209             :   char *frac_name;
     210             :   struct TALER_AmountNBO amount_nbo;
     211             :   enum GNUNET_GenericReturnValue ret;
     212             : 
     213        9610 :   if (sizeof (struct TALER_AmountNBO) != *dst_size)
     214             :   {
     215           0 :     GNUNET_break (0);
     216           0 :     return GNUNET_SYSERR;
     217             :   }
     218        9610 :   GNUNET_asprintf (&val_name,
     219             :                    "%s_val",
     220             :                    fname);
     221        9610 :   GNUNET_asprintf (&frac_name,
     222             :                    "%s_frac",
     223             :                    fname);
     224        9610 :   ret = extract_amount_nbo_helper (result,
     225             :                                    row,
     226             :                                    currency,
     227             :                                    val_name,
     228             :                                    frac_name,
     229             :                                    &amount_nbo);
     230        9610 :   if (GNUNET_OK == ret)
     231        9610 :     TALER_amount_ntoh (r_amount,
     232             :                        &amount_nbo);
     233             :   else
     234           0 :     memset (r_amount,
     235             :             0,
     236             :             sizeof (struct TALER_Amount));
     237        9610 :   GNUNET_free (val_name);
     238        9610 :   GNUNET_free (frac_name);
     239        9610 :   return ret;
     240             : }
     241             : 
     242             : 
     243             : /**
     244             :  * Currency amount expected.
     245             :  *
     246             :  * @param name name of the field in the table
     247             :  * @param currency the currency to use for @a amount
     248             :  * @param[out] amount where to store the result
     249             :  * @return array entry for the result specification to use
     250             :  */
     251             : struct GNUNET_PQ_ResultSpec
     252       12320 : TALER_PQ_result_spec_amount (const char *name,
     253             :                              const char *currency,
     254             :                              struct TALER_Amount *amount)
     255             : {
     256       12320 :   struct GNUNET_PQ_ResultSpec res = {
     257             :     .conv = &extract_amount,
     258             :     .cls = (void *) currency,
     259             :     .dst = (void *) amount,
     260             :     .dst_size = sizeof (*amount),
     261             :     .fname = name
     262             :   };
     263             : 
     264       12320 :   return res;
     265             : }
     266             : 
     267             : 
     268             : /**
     269             :  * Extract data from a Postgres database @a result at row @a row.
     270             :  *
     271             :  * @param cls closure
     272             :  * @param result where to extract data from
     273             :  * @param row row to extract data from
     274             :  * @param fname name (or prefix) of the fields to extract from
     275             :  * @param[in,out] dst_size where to store size of result, may be NULL
     276             :  * @param[out] dst where to store the result
     277             :  * @return
     278             :  *   #GNUNET_YES if all results could be extracted
     279             :  *   #GNUNET_NO if at least one result was NULL
     280             :  *   #GNUNET_SYSERR if a result was invalid (non-existing field)
     281             :  */
     282             : static enum GNUNET_GenericReturnValue
     283         567 : extract_json (void *cls,
     284             :               PGresult *result,
     285             :               int row,
     286             :               const char *fname,
     287             :               size_t *dst_size,
     288             :               void *dst)
     289             : {
     290         567 :   json_t **j_dst = dst;
     291             :   const char *res;
     292             :   int fnum;
     293             :   json_error_t json_error;
     294             :   size_t slen;
     295             : 
     296             :   (void) cls;
     297             :   (void) dst_size;
     298         567 :   fnum = PQfnumber (result,
     299             :                     fname);
     300         567 :   if (fnum < 0)
     301             :   {
     302           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     303             :                 "Field `%s' does not exist in result\n",
     304             :                 fname);
     305           0 :     return GNUNET_SYSERR;
     306             :   }
     307         567 :   if (PQgetisnull (result,
     308             :                    row,
     309             :                    fnum))
     310           0 :     return GNUNET_NO;
     311         567 :   slen = PQgetlength (result,
     312             :                       row,
     313             :                       fnum);
     314         567 :   res = (const char *) PQgetvalue (result,
     315             :                                    row,
     316             :                                    fnum);
     317         567 :   *j_dst = json_loadb (res,
     318             :                        slen,
     319             :                        JSON_REJECT_DUPLICATES,
     320             :                        &json_error);
     321         567 :   if (NULL == *j_dst)
     322             :   {
     323           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     324             :                 "Failed to parse JSON result for field `%s': %s (%s)\n",
     325             :                 fname,
     326             :                 json_error.text,
     327             :                 json_error.source);
     328           0 :     return GNUNET_SYSERR;
     329             :   }
     330         567 :   return GNUNET_OK;
     331             : }
     332             : 
     333             : 
     334             : /**
     335             :  * Function called to clean up memory allocated
     336             :  * by a #GNUNET_PQ_ResultConverter.
     337             :  *
     338             :  * @param cls closure
     339             :  * @param rd result data to clean up
     340             :  */
     341             : static void
     342         348 : clean_json (void *cls,
     343             :             void *rd)
     344             : {
     345         348 :   json_t **dst = rd;
     346             : 
     347             :   (void) cls;
     348         348 :   if (NULL != *dst)
     349             :   {
     350         348 :     json_decref (*dst);
     351         348 :     *dst = NULL;
     352             :   }
     353         348 : }
     354             : 
     355             : 
     356             : /**
     357             :  * json_t expected.
     358             :  *
     359             :  * @param name name of the field in the table
     360             :  * @param[out] jp where to store the result
     361             :  * @return array entry for the result specification to use
     362             :  */
     363             : struct GNUNET_PQ_ResultSpec
     364         615 : TALER_PQ_result_spec_json (const char *name,
     365             :                            json_t **jp)
     366             : {
     367         615 :   struct GNUNET_PQ_ResultSpec res = {
     368             :     .conv = &extract_json,
     369             :     .cleaner = &clean_json,
     370             :     .dst = (void *) jp,
     371             :     .fname  = name
     372             :   };
     373             : 
     374         615 :   return res;
     375             : }
     376             : 
     377             : 
     378             : /**
     379             :  * Extract data from a Postgres database @a result at row @a row.
     380             :  *
     381             :  * @param cls closure
     382             :  * @param result where to extract data from
     383             :  * @param row the row to extract data from
     384             :  * @param fname name (or prefix) of the fields to extract from
     385             :  * @param[in,out] dst_size where to store size of result, may be NULL
     386             :  * @param[out] dst where to store the result
     387             :  * @return
     388             :  *   #GNUNET_YES if all results could be extracted
     389             :  *   #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
     390             :  */
     391             : static enum GNUNET_GenericReturnValue
     392        8397 : extract_round_time (void *cls,
     393             :                     PGresult *result,
     394             :                     int row,
     395             :                     const char *fname,
     396             :                     size_t *dst_size,
     397             :                     void *dst)
     398             : {
     399        8397 :   struct GNUNET_TIME_Absolute *udst = dst;
     400             :   const struct GNUNET_TIME_AbsoluteNBO *res;
     401             :   struct GNUNET_TIME_Absolute tmp;
     402             :   int fnum;
     403             : 
     404             :   (void) cls;
     405        8397 :   fnum = PQfnumber (result,
     406             :                     fname);
     407        8397 :   if (fnum < 0)
     408             :   {
     409           0 :     GNUNET_break (0);
     410           0 :     return GNUNET_SYSERR;
     411             :   }
     412        8397 :   if (PQgetisnull (result,
     413             :                    row,
     414             :                    fnum))
     415           0 :     return GNUNET_NO;
     416        8397 :   GNUNET_assert (NULL != dst);
     417        8397 :   if (sizeof (struct GNUNET_TIME_Absolute) != *dst_size)
     418             :   {
     419           0 :     GNUNET_break (0);
     420           0 :     return GNUNET_SYSERR;
     421             :   }
     422        8397 :   res = (struct GNUNET_TIME_AbsoluteNBO *) PQgetvalue (result,
     423             :                                                        row,
     424             :                                                        fnum);
     425        8397 :   tmp = GNUNET_TIME_absolute_ntoh (*res);
     426        8397 :   GNUNET_break (GNUNET_OK ==
     427             :                 GNUNET_TIME_round_abs (&tmp));
     428        8397 :   *udst = tmp;
     429        8397 :   return GNUNET_OK;
     430             : }
     431             : 
     432             : 
     433             : /**
     434             :  * Rounded absolute time expected.
     435             :  * In contrast to #GNUNET_PQ_query_param_absolute_time_nbo(),
     436             :  * this function ensures that the result is rounded and can
     437             :  * be converted to JSON.
     438             :  *
     439             :  * @param name name of the field in the table
     440             :  * @param[out] at where to store the result
     441             :  * @return array entry for the result specification to use
     442             :  */
     443             : struct GNUNET_PQ_ResultSpec
     444        9560 : TALER_PQ_result_spec_absolute_time (const char *name,
     445             :                                     struct GNUNET_TIME_Absolute *at)
     446             : {
     447        9560 :   struct GNUNET_PQ_ResultSpec res = {
     448             :     .conv = &extract_round_time,
     449             :     .dst = (void *) at,
     450             :     .dst_size = sizeof (struct GNUNET_TIME_Absolute),
     451             :     .fname = name
     452             :   };
     453             : 
     454        9560 :   return res;
     455             : }
     456             : 
     457             : 
     458             : /**
     459             :  * Extract data from a Postgres database @a result at row @a row.
     460             :  *
     461             :  * @param cls closure
     462             :  * @param result where to extract data from
     463             :  * @param row the row to extract data from
     464             :  * @param fname name (or prefix) of the fields to extract from
     465             :  * @param[in,out] dst_size where to store size of result, may be NULL
     466             :  * @param[out] dst where to store the result
     467             :  * @return
     468             :  *   #GNUNET_YES if all results could be extracted
     469             :  *   #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
     470             :  */
     471             : static enum GNUNET_GenericReturnValue
     472      297472 : extract_round_time_nbo (void *cls,
     473             :                         PGresult *result,
     474             :                         int row,
     475             :                         const char *fname,
     476             :                         size_t *dst_size,
     477             :                         void *dst)
     478             : {
     479      297472 :   struct GNUNET_TIME_AbsoluteNBO *udst = dst;
     480             :   const struct GNUNET_TIME_AbsoluteNBO *res;
     481             :   struct GNUNET_TIME_Absolute tmp;
     482             :   int fnum;
     483             : 
     484             :   (void) cls;
     485      297472 :   fnum = PQfnumber (result,
     486             :                     fname);
     487      297472 :   if (fnum < 0)
     488             :   {
     489           0 :     GNUNET_break (0);
     490           0 :     return GNUNET_SYSERR;
     491             :   }
     492      297472 :   if (PQgetisnull (result,
     493             :                    row,
     494             :                    fnum))
     495           0 :     return GNUNET_NO;
     496      297472 :   GNUNET_assert (NULL != dst);
     497      297472 :   if (sizeof (struct GNUNET_TIME_AbsoluteNBO) != *dst_size)
     498             :   {
     499           0 :     GNUNET_break (0);
     500           0 :     return GNUNET_SYSERR;
     501             :   }
     502      297472 :   res = (struct GNUNET_TIME_AbsoluteNBO *) PQgetvalue (result,
     503             :                                                        row,
     504             :                                                        fnum);
     505      297472 :   tmp = GNUNET_TIME_absolute_ntoh (*res);
     506      297472 :   GNUNET_break (GNUNET_OK ==
     507             :                 GNUNET_TIME_round_abs (&tmp));
     508      297472 :   *udst = GNUNET_TIME_absolute_hton (tmp);
     509      297472 :   return GNUNET_OK;
     510             : }
     511             : 
     512             : 
     513             : /**
     514             :  * Rounded absolute time in network byte order expected.
     515             :  * In contrast to #GNUNET_PQ_query_param_absolute_time_nbo(),
     516             :  * this function ensures that the result is rounded and can
     517             :  * be converted to JSON.
     518             :  *
     519             :  * @param name name of the field in the table
     520             :  * @param[out] at where to store the result
     521             :  * @return array entry for the result specification to use
     522             :  */
     523             : struct GNUNET_PQ_ResultSpec
     524      297476 : TALER_PQ_result_spec_absolute_time_nbo (const char *name,
     525             :                                         struct GNUNET_TIME_AbsoluteNBO *at)
     526             : {
     527      297476 :   struct GNUNET_PQ_ResultSpec res = {
     528             :     .conv = &extract_round_time_nbo,
     529             :     .dst = (void *) at,
     530             :     .dst_size = sizeof (struct GNUNET_TIME_AbsoluteNBO),
     531             :     .fname = name
     532             :   };
     533             : 
     534      297476 :   return res;
     535             : }
     536             : 
     537             : 
     538             : /* end of pq_result_helper.c */

Generated by: LCOV version 1.14