LCOV - code coverage report
Current view: top level - pq - pq_result_helper.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 97 122 79.5 %
Date: 2019-07-18 06:05:54 Functions: 12 12 100.0 %

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

Generated by: LCOV version 1.13