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

Generated by: LCOV version 1.14