LCOV - code coverage report
Current view: top level - pq - pq_query_helper.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 232 363 63.9 %
Date: 2025-06-05 21:03:14 Functions: 16 24 66.7 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2023 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_query_helper.c
      18             :  * @brief helper functions for Taler-specific libpq (PostGres) interactions
      19             :  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
      20             :  * @author Florian Dold
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "platform.h"
      24             : #include <gnunet/gnunet_common.h>
      25             : #include <gnunet/gnunet_util_lib.h>
      26             : #include <gnunet/gnunet_pq_lib.h>
      27             : #include "taler_pq_lib.h"
      28             : #include "pq_common.h"
      29             : 
      30             : 
      31             : /**
      32             :  * Function called to convert input amount into SQL parameter as tuple.
      33             :  *
      34             :  * @param cls closure
      35             :  * @param data pointer to input argument, here a `struct TALER_Amount`
      36             :  * @param data_len number of bytes in @a data (if applicable)
      37             :  * @param[out] param_values SQL data to set
      38             :  * @param[out] param_lengths SQL length data to set
      39             :  * @param[out] param_formats SQL format data to set
      40             :  * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
      41             :  * @param[out] scratch buffer for dynamic allocations (to be done via GNUNET_malloc()
      42             :  * @param scratch_length number of entries left in @a scratch
      43             :  * @return -1 on error, number of offsets used in @a scratch otherwise
      44             :  */
      45             : static int
      46          49 : qconv_amount_currency_tuple (void *cls,
      47             :                              const void *data,
      48             :                              size_t data_len,
      49             :                              void *param_values[],
      50             :                              int param_lengths[],
      51             :                              int param_formats[],
      52             :                              unsigned int param_length,
      53             :                              void *scratch[],
      54             :                              unsigned int scratch_length)
      55             : {
      56          49 :   struct GNUNET_PQ_Context *db = cls;
      57          49 :   const struct TALER_Amount *amount = data;
      58             :   size_t sz;
      59             : 
      60          49 :   GNUNET_assert (NULL != db);
      61          49 :   GNUNET_assert (NULL != amount);
      62          49 :   GNUNET_assert (1 == param_length);
      63          49 :   GNUNET_assert (1 <= scratch_length);
      64          49 :   GNUNET_assert (sizeof (struct TALER_Amount) == data_len);
      65             :   GNUNET_static_assert (sizeof(uint32_t) == sizeof(Oid));
      66             :   {
      67             :     char *out;
      68             :     Oid oid_v;
      69             :     Oid oid_f;
      70             :     Oid oid_c;
      71             :     struct TALER_PQ_AmountCurrencyP d;
      72             : 
      73          49 :     GNUNET_assert (GNUNET_OK ==
      74             :                    GNUNET_PQ_get_oid_by_name (db,
      75             :                                               "int8",
      76             :                                               &oid_v));
      77          49 :     GNUNET_assert (GNUNET_OK ==
      78             :                    GNUNET_PQ_get_oid_by_name (db,
      79             :                                               "int4",
      80             :                                               &oid_f));
      81          49 :     GNUNET_assert (GNUNET_OK ==
      82             :                    GNUNET_PQ_get_oid_by_name (db,
      83             :                                               "varchar",
      84             :                                               &oid_c));
      85          49 :     sz = TALER_PQ_make_taler_pq_amount_currency_ (amount,
      86             :                                                   oid_v,
      87             :                                                   oid_f,
      88             :                                                   oid_c,
      89             :                                                   &d);
      90          49 :     out = GNUNET_malloc (sz);
      91          49 :     memcpy (out,
      92             :             &d,
      93             :             sz);
      94          49 :     scratch[0] = out;
      95             :   }
      96             : 
      97          49 :   param_values[0] = scratch[0];
      98          49 :   param_lengths[0] = sz;
      99          49 :   param_formats[0] = 1;
     100             : 
     101          49 :   return 1;
     102             : }
     103             : 
     104             : 
     105             : struct GNUNET_PQ_QueryParam
     106          49 : TALER_PQ_query_param_amount_with_currency (
     107             :   const struct GNUNET_PQ_Context *db,
     108             :   const struct TALER_Amount *amount)
     109             : {
     110          49 :   struct GNUNET_PQ_QueryParam res = {
     111             :     .conv_cls = (void *) db,
     112             :     .conv = &qconv_amount_currency_tuple,
     113             :     .data = amount,
     114             :     .size = sizeof (*amount),
     115             :     .num_params = 1,
     116             :   };
     117             : 
     118          49 :   return res;
     119             : }
     120             : 
     121             : 
     122             : /**
     123             :  * Function called to convert input amount into SQL parameter as tuple.
     124             :  *
     125             :  * @param cls closure
     126             :  * @param data pointer to input argument, here a `struct TALER_Amount`
     127             :  * @param data_len number of bytes in @a data (if applicable)
     128             :  * @param[out] param_values SQL data to set
     129             :  * @param[out] param_lengths SQL length data to set
     130             :  * @param[out] param_formats SQL format data to set
     131             :  * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
     132             :  * @param[out] scratch buffer for dynamic allocations (to be done via GNUNET_malloc()
     133             :  * @param scratch_length number of entries left in @a scratch
     134             :  * @return -1 on error, number of offsets used in @a scratch otherwise
     135             :  */
     136             : static int
     137       35848 : qconv_amount_tuple (void *cls,
     138             :                     const void *data,
     139             :                     size_t data_len,
     140             :                     void *param_values[],
     141             :                     int param_lengths[],
     142             :                     int param_formats[],
     143             :                     unsigned int param_length,
     144             :                     void *scratch[],
     145             :                     unsigned int scratch_length)
     146             : {
     147       35848 :   struct GNUNET_PQ_Context *db = cls;
     148       35848 :   const struct TALER_Amount *amount = data;
     149             :   size_t sz;
     150             : 
     151       35848 :   GNUNET_assert (NULL != db);
     152       35848 :   GNUNET_assert (NULL != amount);
     153       35848 :   GNUNET_assert (1 == param_length);
     154       35848 :   GNUNET_assert (1 <= scratch_length);
     155       35848 :   GNUNET_assert (sizeof (struct TALER_Amount) == data_len);
     156             :   GNUNET_static_assert (sizeof(uint32_t) == sizeof(Oid));
     157             :   {
     158             :     char *out;
     159             :     Oid oid_v;
     160             :     Oid oid_f;
     161             : 
     162       35848 :     GNUNET_assert (GNUNET_OK ==
     163             :                    GNUNET_PQ_get_oid_by_name (db,
     164             :                                               "int8",
     165             :                                               &oid_v));
     166       35848 :     GNUNET_assert (GNUNET_OK ==
     167             :                    GNUNET_PQ_get_oid_by_name (db,
     168             :                                               "int4",
     169             :                                               &oid_f));
     170             : 
     171             :     {
     172             :       struct TALER_PQ_AmountP d
     173       35848 :         = TALER_PQ_make_taler_pq_amount_ (amount,
     174             :                                           oid_v,
     175             :                                           oid_f);
     176             : 
     177       35848 :       sz = sizeof(d);
     178       35848 :       out = GNUNET_malloc (sz);
     179       35848 :       scratch[0] = out;
     180       35848 :       GNUNET_memcpy (out,
     181             :                      &d,
     182             :                      sizeof(d));
     183             :     }
     184             :   }
     185             : 
     186       35848 :   param_values[0] = scratch[0];
     187       35848 :   param_lengths[0] = sz;
     188       35848 :   param_formats[0] = 1;
     189             : 
     190       35848 :   return 1;
     191             : }
     192             : 
     193             : 
     194             : struct GNUNET_PQ_QueryParam
     195       35848 : TALER_PQ_query_param_amount (
     196             :   const struct GNUNET_PQ_Context *db,
     197             :   const struct TALER_Amount *amount)
     198             : {
     199       35848 :   struct GNUNET_PQ_QueryParam res = {
     200             :     .conv_cls = (void *) db,
     201             :     .conv = &qconv_amount_tuple,
     202             :     .data = amount,
     203             :     .size = sizeof (*amount),
     204             :     .num_params = 1,
     205             :   };
     206             : 
     207       35848 :   return res;
     208             : }
     209             : 
     210             : 
     211             : /**
     212             :  * Function called to convert input argument into SQL parameters.
     213             :  *
     214             :  * @param cls closure
     215             :  * @param data pointer to input argument
     216             :  * @param data_len number of bytes in @a data (if applicable)
     217             :  * @param[out] param_values SQL data to set
     218             :  * @param[out] param_lengths SQL length data to set
     219             :  * @param[out] param_formats SQL format data to set
     220             :  * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
     221             :  * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
     222             :  * @param scratch_length number of entries left in @a scratch
     223             :  * @return -1 on error, number of offsets used in @a scratch otherwise
     224             :  */
     225             : static int
     226        6668 : qconv_denom_pub (void *cls,
     227             :                  const void *data,
     228             :                  size_t data_len,
     229             :                  void *param_values[],
     230             :                  int param_lengths[],
     231             :                  int param_formats[],
     232             :                  unsigned int param_length,
     233             :                  void *scratch[],
     234             :                  unsigned int scratch_length)
     235             : {
     236        6668 :   const struct TALER_DenominationPublicKey *denom_pub = data;
     237        6668 :   const struct GNUNET_CRYPTO_BlindSignPublicKey *bsp = denom_pub->bsign_pub_key;
     238             :   size_t tlen;
     239             :   size_t len;
     240             :   uint32_t be[2];
     241             :   char *buf;
     242             :   void *tbuf;
     243             : 
     244             :   (void) cls;
     245             :   (void) data_len;
     246        6668 :   GNUNET_assert (1 == param_length);
     247        6668 :   GNUNET_assert (scratch_length > 0);
     248        6668 :   GNUNET_break (NULL == cls);
     249        6668 :   be[0] = htonl ((uint32_t) bsp->cipher);
     250        6668 :   be[1] = htonl (denom_pub->age_mask.bits);
     251        6668 :   switch (bsp->cipher)
     252             :   {
     253        4006 :   case GNUNET_CRYPTO_BSA_RSA:
     254        4006 :     tlen = GNUNET_CRYPTO_rsa_public_key_encode (
     255        4006 :       bsp->details.rsa_public_key,
     256             :       &tbuf);
     257        4006 :     break;
     258        2662 :   case GNUNET_CRYPTO_BSA_CS:
     259        2662 :     tlen = sizeof (bsp->details.cs_public_key);
     260        2662 :     break;
     261           0 :   default:
     262           0 :     GNUNET_assert (0);
     263             :   }
     264        6668 :   len = tlen + sizeof (be);
     265        6668 :   buf = GNUNET_malloc (len);
     266        6668 :   GNUNET_memcpy (buf,
     267             :                  be,
     268             :                  sizeof (be));
     269        6668 :   switch (bsp->cipher)
     270             :   {
     271        4006 :   case GNUNET_CRYPTO_BSA_RSA:
     272        4006 :     GNUNET_memcpy (&buf[sizeof (be)],
     273             :                    tbuf,
     274             :                    tlen);
     275        4006 :     GNUNET_free (tbuf);
     276        4006 :     break;
     277        2662 :   case GNUNET_CRYPTO_BSA_CS:
     278        2662 :     GNUNET_memcpy (&buf[sizeof (be)],
     279             :                    &bsp->details.cs_public_key,
     280             :                    tlen);
     281        2662 :     break;
     282           0 :   default:
     283           0 :     GNUNET_assert (0);
     284             :   }
     285             : 
     286        6668 :   scratch[0] = buf;
     287        6668 :   param_values[0] = (void *) buf;
     288        6668 :   param_lengths[0] = len;
     289        6668 :   param_formats[0] = 1;
     290        6668 :   return 1;
     291             : }
     292             : 
     293             : 
     294             : struct GNUNET_PQ_QueryParam
     295        6668 : TALER_PQ_query_param_denom_pub (
     296             :   const struct TALER_DenominationPublicKey *denom_pub)
     297             : {
     298        6668 :   struct GNUNET_PQ_QueryParam res = {
     299             :     .conv = &qconv_denom_pub,
     300             :     .data = denom_pub,
     301             :     .num_params = 1
     302             :   };
     303             : 
     304        6668 :   return res;
     305             : }
     306             : 
     307             : 
     308             : struct GNUNET_PQ_QueryParam
     309         186 : TALER_PQ_query_param_denom_sig (
     310             :   const struct TALER_DenominationSignature *denom_sig)
     311             : {
     312         186 :   return GNUNET_PQ_query_param_unblinded_sig (denom_sig->unblinded_sig);
     313             : }
     314             : 
     315             : 
     316             : struct GNUNET_PQ_QueryParam
     317           0 : TALER_PQ_query_param_blinded_denom_sig (
     318             :   const struct TALER_BlindedDenominationSignature *denom_sig)
     319             : {
     320           0 :   return GNUNET_PQ_query_param_blinded_sig (denom_sig->blinded_sig);
     321             : }
     322             : 
     323             : 
     324             : /**
     325             :  * Function called to convert input argument into SQL parameters.
     326             :  *
     327             :  * @param cls closure
     328             :  * @param data pointer to input argument
     329             :  * @param data_len number of bytes in @a data (if applicable)
     330             :  * @param[out] param_values SQL data to set
     331             :  * @param[out] param_lengths SQL length data to set
     332             :  * @param[out] param_formats SQL format data to set
     333             :  * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
     334             :  * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
     335             :  * @param scratch_length number of entries left in @a scratch
     336             :  * @return -1 on error, number of offsets used in @a scratch otherwise
     337             :  */
     338             : static int
     339           0 : qconv_blinded_planchet (void *cls,
     340             :                         const void *data,
     341             :                         size_t data_len,
     342             :                         void *param_values[],
     343             :                         int param_lengths[],
     344             :                         int param_formats[],
     345             :                         unsigned int param_length,
     346             :                         void *scratch[],
     347             :                         unsigned int scratch_length)
     348             : {
     349           0 :   const struct TALER_BlindedPlanchet *bp = data;
     350           0 :   const struct GNUNET_CRYPTO_BlindedMessage *bm = bp->blinded_message;
     351             :   size_t tlen;
     352             :   size_t len;
     353             :   uint32_t be[2];
     354             :   char *buf;
     355             : 
     356             :   (void) cls;
     357             :   (void) data_len;
     358           0 :   GNUNET_assert (1 == param_length);
     359           0 :   GNUNET_assert (scratch_length > 0);
     360           0 :   GNUNET_break (NULL == cls);
     361           0 :   be[0] = htonl ((uint32_t) bm->cipher);
     362           0 :   be[1] = htonl (0x0100); /* magic marker: blinded */
     363           0 :   switch (bm->cipher)
     364             :   {
     365           0 :   case GNUNET_CRYPTO_BSA_RSA:
     366           0 :     tlen = bm->details.rsa_blinded_message.blinded_msg_size;
     367           0 :     break;
     368           0 :   case GNUNET_CRYPTO_BSA_CS:
     369           0 :     tlen = sizeof (bm->details.cs_blinded_message);
     370           0 :     break;
     371           0 :   default:
     372           0 :     GNUNET_assert (0);
     373             :   }
     374           0 :   len = tlen + sizeof (be);
     375           0 :   buf = GNUNET_malloc (len);
     376           0 :   GNUNET_memcpy (buf,
     377             :                  &be,
     378             :                  sizeof (be));
     379           0 :   switch (bm->cipher)
     380             :   {
     381           0 :   case GNUNET_CRYPTO_BSA_RSA:
     382           0 :     GNUNET_memcpy (&buf[sizeof (be)],
     383             :                    bm->details.rsa_blinded_message.blinded_msg,
     384             :                    tlen);
     385           0 :     break;
     386           0 :   case GNUNET_CRYPTO_BSA_CS:
     387           0 :     GNUNET_memcpy (&buf[sizeof (be)],
     388             :                    &bm->details.cs_blinded_message,
     389             :                    tlen);
     390           0 :     break;
     391           0 :   default:
     392           0 :     GNUNET_assert (0);
     393             :   }
     394           0 :   scratch[0] = buf;
     395           0 :   param_values[0] = (void *) buf;
     396           0 :   param_lengths[0] = len;
     397           0 :   param_formats[0] = 1;
     398           0 :   return 1;
     399             : }
     400             : 
     401             : 
     402             : struct GNUNET_PQ_QueryParam
     403           0 : TALER_PQ_query_param_blinded_planchet (
     404             :   const struct TALER_BlindedPlanchet *bp)
     405             : {
     406           0 :   struct GNUNET_PQ_QueryParam res = {
     407             :     .conv = &qconv_blinded_planchet,
     408             :     .data = bp,
     409             :     .num_params = 1
     410             :   };
     411             : 
     412           0 :   return res;
     413             : }
     414             : 
     415             : 
     416             : /**
     417             :  * Function called to convert input argument into SQL parameters.
     418             :  *
     419             :  * @param cls closure
     420             :  * @param data pointer to input argument
     421             :  * @param data_len number of bytes in @a data (if applicable)
     422             :  * @param[out] param_values SQL data to set
     423             :  * @param[out] param_lengths SQL length data to set
     424             :  * @param[out] param_formats SQL format data to set
     425             :  * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
     426             :  * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
     427             :  * @param scratch_length number of entries left in @a scratch
     428             :  * @return -1 on error, number of offsets used in @a scratch otherwise
     429             :  */
     430             : static int
     431           0 : qconv_exchange_blinding_values (void *cls,
     432             :                                 const void *data,
     433             :                                 size_t data_len,
     434             :                                 void *param_values[],
     435             :                                 int param_lengths[],
     436             :                                 int param_formats[],
     437             :                                 unsigned int param_length,
     438             :                                 void *scratch[],
     439             :                                 unsigned int scratch_length)
     440             : {
     441           0 :   const struct TALER_ExchangeBlindingValues *blinding_values = data;
     442           0 :   const struct GNUNET_CRYPTO_BlindingInputValues *bi =
     443             :     blinding_values->blinding_inputs;
     444             :   size_t tlen;
     445             :   size_t len;
     446             :   uint32_t be[2];
     447             :   char *buf;
     448             : 
     449             :   (void) cls;
     450             :   (void) data_len;
     451           0 :   GNUNET_assert (1 == param_length);
     452           0 :   GNUNET_assert (scratch_length > 0);
     453           0 :   GNUNET_break (NULL == cls);
     454           0 :   be[0] = htonl ((uint32_t) bi->cipher);
     455           0 :   be[1] = htonl (0x010000); /* magic marker: EWV */
     456           0 :   switch (bi->cipher)
     457             :   {
     458           0 :   case GNUNET_CRYPTO_BSA_RSA:
     459           0 :     tlen = 0;
     460           0 :     break;
     461           0 :   case GNUNET_CRYPTO_BSA_CS:
     462           0 :     tlen = sizeof (struct GNUNET_CRYPTO_CSPublicRPairP);
     463           0 :     break;
     464           0 :   default:
     465           0 :     GNUNET_assert (0);
     466             :   }
     467           0 :   len = tlen + sizeof (be);
     468           0 :   buf = GNUNET_malloc (len);
     469           0 :   GNUNET_memcpy (buf,
     470             :                  &be,
     471             :                  sizeof (be));
     472           0 :   switch (bi->cipher)
     473             :   {
     474           0 :   case GNUNET_CRYPTO_BSA_RSA:
     475           0 :     break;
     476           0 :   case GNUNET_CRYPTO_BSA_CS:
     477           0 :     GNUNET_memcpy (&buf[sizeof (be)],
     478             :                    &bi->details.cs_values,
     479             :                    tlen);
     480           0 :     break;
     481           0 :   default:
     482           0 :     GNUNET_assert (0);
     483             :   }
     484           0 :   scratch[0] = buf;
     485           0 :   param_values[0] = (void *) buf;
     486           0 :   param_lengths[0] = len;
     487           0 :   param_formats[0] = 1;
     488           0 :   return 1;
     489             : }
     490             : 
     491             : 
     492             : struct GNUNET_PQ_QueryParam
     493           0 : TALER_PQ_query_param_exchange_blinding_values (
     494             :   const struct TALER_ExchangeBlindingValues *blinding_values)
     495             : {
     496           0 :   struct GNUNET_PQ_QueryParam res = {
     497             :     .conv = &qconv_exchange_blinding_values,
     498             :     .data = blinding_values,
     499             :     .num_params = 1
     500             :   };
     501             : 
     502           0 :   return res;
     503             : }
     504             : 
     505             : 
     506             : /**
     507             :  * Function called to convert input argument into SQL parameters.
     508             :  *
     509             :  * @param cls closure
     510             :  * @param data pointer to input argument, here a `json_t *`
     511             :  * @param data_len number of bytes in @a data (if applicable)
     512             :  * @param[out] param_values SQL data to set
     513             :  * @param[out] param_lengths SQL length data to set
     514             :  * @param[out] param_formats SQL format data to set
     515             :  * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
     516             :  * @param[out] scratch buffer for dynamic allocations (to be done via GNUNET_malloc()
     517             :  * @param scratch_length number of entries left in @a scratch
     518             :  * @return -1 on error, number of offsets used in @a scratch otherwise
     519             :  */
     520             : static int
     521         122 : qconv_json (void *cls,
     522             :             const void *data,
     523             :             size_t data_len,
     524             :             void *param_values[],
     525             :             int param_lengths[],
     526             :             int param_formats[],
     527             :             unsigned int param_length,
     528             :             void *scratch[],
     529             :             unsigned int scratch_length)
     530             : {
     531         122 :   const json_t *json = data;
     532             :   char *str;
     533             : 
     534             :   (void) cls;
     535             :   (void) data_len;
     536         122 :   GNUNET_assert (1 == param_length);
     537         122 :   GNUNET_assert (scratch_length > 0);
     538         122 :   str = json_dumps (json,
     539             :                     JSON_COMPACT);
     540         122 :   if (NULL == str)
     541             :   {
     542           0 :     GNUNET_break (0);
     543           0 :     return -1;
     544             :   }
     545         122 :   scratch[0] = str;
     546         122 :   param_values[0] = (void *) str;
     547         122 :   param_lengths[0] = strlen (str);
     548         122 :   param_formats[0] = 1;
     549         122 :   return 1;
     550             : }
     551             : 
     552             : 
     553             : struct GNUNET_PQ_QueryParam
     554         116 : TALER_PQ_query_param_json (const json_t *x)
     555             : {
     556         116 :   struct GNUNET_PQ_QueryParam res = {
     557             :     .conv = &qconv_json,
     558             :     .data = x,
     559             :     .num_params = 1
     560             :   };
     561             : 
     562         116 :   return res;
     563             : }
     564             : 
     565             : 
     566             : /** ------------------- Array support  -----------------------------------**/
     567             : 
     568             : /**
     569             :  * Closure for the array type handlers.
     570             :  *
     571             :  * May contain sizes information for the data, given (and handled) by the
     572             :  * caller.
     573             :  */
     574             : struct qconv_array_cls
     575             : {
     576             :   /**
     577             :    * If not null, contains the array of sizes (the size of the array is the
     578             :    * .size field in the ambient GNUNET_PQ_QueryParam struct). We do not free
     579             :    * this memory.
     580             :    *
     581             :    * If not null, this value has precedence over @a sizes, which MUST be NULL */
     582             :   const size_t *sizes;
     583             : 
     584             :   /**
     585             :    * If @a size and @a c_sizes are NULL, this field defines the same size
     586             :    * for each element in the array.
     587             :    */
     588             :   size_t same_size;
     589             : 
     590             :   /**
     591             :    * If true, the array parameter to the data pointer to the qconv_array is a
     592             :    * continuous byte array of data, either with @a same_size each or sizes
     593             :    * provided bytes by @a sizes;
     594             :    */
     595             :   bool continuous;
     596             : 
     597             :   /**
     598             :    * Type of the array elements
     599             :    */
     600             :   enum TALER_PQ_ArrayType typ;
     601             : 
     602             :   /**
     603             :    * Oid of the array elements
     604             :    */
     605             :   Oid oid;
     606             : 
     607             :   /**
     608             :    * db context, needed for OID-lookup of basis-types
     609             :    */
     610             :   struct GNUNET_PQ_Context *db;
     611             : };
     612             : 
     613             : /**
     614             :  * Callback to cleanup a qconv_array_cls to be used during
     615             :  * GNUNET_PQ_cleanup_query_params_closures
     616             :  */
     617             : static void
     618        1233 : qconv_array_cls_cleanup (void *cls)
     619             : {
     620        1233 :   GNUNET_free (cls);
     621        1233 : }
     622             : 
     623             : 
     624             : /**
     625             :  * Function called to convert input argument into SQL parameters for arrays
     626             :  *
     627             :  * Note: the format for the encoding of arrays for libpq is not very well
     628             :  * documented.  We peeked into various sources (postgresql and libpqtypes) for
     629             :  * guidance.
     630             :  *
     631             :  * @param cls Closure of type struct qconv_array_cls*
     632             :  * @param data Pointer to first element in the array
     633             :  * @param data_len Number of _elements_ in array @a data (if applicable)
     634             :  * @param[out] param_values SQL data to set
     635             :  * @param[out] param_lengths SQL length data to set
     636             :  * @param[out] param_formats SQL format data to set
     637             :  * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
     638             :  * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
     639             :  * @param scratch_length number of entries left in @a scratch
     640             :  * @return -1 on error, number of offsets used in @a scratch otherwise
     641             :  */
     642             : static int
     643        1240 : qconv_array (
     644             :   void *cls,
     645             :   const void *data,
     646             :   size_t data_len,
     647             :   void *param_values[],
     648             :   int param_lengths[],
     649             :   int param_formats[],
     650             :   unsigned int param_length,
     651             :   void *scratch[],
     652             :   unsigned int scratch_length)
     653             : {
     654        1240 :   struct qconv_array_cls *meta = cls;
     655        1240 :   size_t num = data_len;
     656             :   size_t total_size;
     657             :   const size_t *sizes;
     658             :   bool same_sized;
     659        1240 :   void *elements = NULL;
     660        1240 :   bool noerror = true;
     661             :   /* needed to capture the encoded rsa signatures */
     662        1240 :   void **buffers = NULL;
     663        1240 :   size_t *buffer_lengths = NULL;
     664             : 
     665             :   (void) (param_length);
     666             :   (void) (scratch_length);
     667             : 
     668        1240 :   GNUNET_assert (NULL != meta);
     669        1240 :   GNUNET_assert (num < INT_MAX);
     670             : 
     671        1240 :   sizes = meta->sizes;
     672        1240 :   same_sized = (0 != meta->same_size);
     673             : 
     674             : #define RETURN_UNLESS(cond) \
     675             :         do { \
     676             :           if (! (cond)) \
     677             :           { \
     678             :             GNUNET_break ((cond)); \
     679             :             noerror = false; \
     680             :             goto DONE; \
     681             :           } \
     682             :         } while (0)
     683             : 
     684             :   /* Calculate sizes and check bounds */
     685             :   {
     686             :     /* num * length-field */
     687        1240 :     size_t x = sizeof(uint32_t);
     688        1240 :     size_t y = x * num;
     689        1240 :     RETURN_UNLESS ((0 == num) || (y / num == x));
     690             : 
     691             :     /* size of header */
     692        1240 :     total_size  = x = sizeof(struct GNUNET_PQ_ArrayHeader_P);
     693        1240 :     total_size += y;
     694        1240 :     RETURN_UNLESS (total_size >= x);
     695             : 
     696             :     /* sizes of elements */
     697        1240 :     if (same_sized)
     698             :     {
     699        1122 :       x = num * meta->same_size;
     700        1122 :       RETURN_UNLESS ((0 == num) || (x / num == meta->same_size));
     701             : 
     702        1122 :       y = total_size;
     703        1122 :       total_size += x;
     704        1122 :       RETURN_UNLESS (total_size >= y);
     705             :     }
     706             :     else  /* sizes are different per element */
     707             :     {
     708         118 :       switch (meta->typ)
     709             :       {
     710           0 :       case TALER_PQ_array_of_amount_currency:
     711             :         {
     712           0 :           const struct TALER_Amount *amounts = data;
     713             :           Oid oid_v;
     714             :           Oid oid_f;
     715             :           Oid oid_c;
     716             : 
     717           0 :           buffer_lengths  = GNUNET_new_array (num, size_t);
     718             :           /* hoist out of loop? */
     719           0 :           GNUNET_assert (GNUNET_OK ==
     720             :                          GNUNET_PQ_get_oid_by_name (meta->db,
     721             :                                                     "int8",
     722             :                                                     &oid_v));
     723           0 :           GNUNET_assert (GNUNET_OK ==
     724             :                          GNUNET_PQ_get_oid_by_name (meta->db,
     725             :                                                     "int4",
     726             :                                                     &oid_f));
     727           0 :           GNUNET_assert (GNUNET_OK ==
     728             :                          GNUNET_PQ_get_oid_by_name (meta->db,
     729             :                                                     "varchar",
     730             :                                                     &oid_c));
     731           0 :           for (size_t i = 0; i<num; i++)
     732             :           {
     733             :             struct TALER_PQ_AmountCurrencyP am;
     734             :             size_t len;
     735             : 
     736           0 :             len = TALER_PQ_make_taler_pq_amount_currency_ (
     737           0 :               &amounts[i],
     738             :               oid_v,
     739             :               oid_f,
     740             :               oid_c,
     741             :               &am);
     742           0 :             buffer_lengths[i] = len;
     743           0 :             y = total_size;
     744           0 :             total_size += len;
     745           0 :             RETURN_UNLESS (total_size >= y);
     746             :           }
     747           0 :           sizes = buffer_lengths;
     748           0 :           break;
     749             :         }
     750         118 :       case TALER_PQ_array_of_blinded_denom_sig:
     751             :         {
     752         118 :           const struct TALER_BlindedDenominationSignature *denom_sigs = data;
     753             :           size_t len;
     754             : 
     755         118 :           buffers  = GNUNET_new_array (num, void *);
     756         118 :           buffer_lengths  = GNUNET_new_array (num, size_t);
     757             : 
     758         521 :           for (size_t i = 0; i<num; i++)
     759             :           {
     760         403 :             const struct GNUNET_CRYPTO_BlindedSignature *bs =
     761         403 :               denom_sigs[i].blinded_sig;
     762             : 
     763         403 :             switch (bs->cipher)
     764             :             {
     765         306 :             case GNUNET_CRYPTO_BSA_RSA:
     766         306 :               len = GNUNET_CRYPTO_rsa_signature_encode (
     767         306 :                 bs->details.blinded_rsa_signature,
     768         306 :                 &buffers[i]);
     769         306 :               RETURN_UNLESS (len != 0);
     770         306 :               break;
     771          97 :             case GNUNET_CRYPTO_BSA_CS:
     772          97 :               len = sizeof (bs->details.blinded_cs_answer);
     773          97 :               break;
     774           0 :             default:
     775           0 :               GNUNET_assert (0);
     776             :             }
     777             : 
     778             :             /* for the cipher and marker */
     779         403 :             len += 2 * sizeof(uint32_t);
     780         403 :             buffer_lengths[i] = len;
     781             : 
     782         403 :             y = total_size;
     783         403 :             total_size += len;
     784         403 :             RETURN_UNLESS (total_size >= y);
     785             :           }
     786         118 :           sizes = buffer_lengths;
     787         118 :           break;
     788             :         }
     789           0 :       default:
     790           0 :         GNUNET_assert (0);
     791             :       }
     792             :     }
     793             : 
     794        1240 :     RETURN_UNLESS (INT_MAX > total_size);
     795        1240 :     RETURN_UNLESS (0 != total_size);
     796             : 
     797        1240 :     elements = GNUNET_malloc (total_size);
     798             :   }
     799             : 
     800             :   /* Write data */
     801             :   {
     802        1240 :     char *out = elements;
     803        1240 :     struct GNUNET_PQ_ArrayHeader_P h = {
     804        1240 :       .ndim = htonl (1),        /* We only support one-dimensional arrays */
     805        1240 :       .has_null = htonl (0),    /* We do not support NULL entries in arrays */
     806        1240 :       .lbound = htonl (1),      /* Default start index value */
     807        1240 :       .dim = htonl (num),
     808        1240 :       .oid = htonl (meta->oid),
     809             :     };
     810             : 
     811             :     /* Write header */
     812        1240 :     GNUNET_memcpy (out,
     813             :                    &h,
     814             :                    sizeof(h));
     815        1240 :     out += sizeof(h);
     816             : 
     817             :     /* Write elements */
     818        8784 :     for (size_t i = 0; i < num; i++)
     819             :     {
     820        7544 :       size_t sz = same_sized ? meta->same_size : sizes[i];
     821             : 
     822        7544 :       *(uint32_t *) out = htonl (sz);
     823        7544 :       out += sizeof(uint32_t);
     824        7544 :       switch (meta->typ)
     825             :       {
     826        7037 :       case TALER_PQ_array_of_amount:
     827             :         {
     828        7037 :           const struct TALER_Amount *amounts = data;
     829             :           Oid oid_v;
     830             :           Oid oid_f;
     831             : 
     832             :           /* hoist out of loop? */
     833        7037 :           GNUNET_assert (GNUNET_OK ==
     834             :                          GNUNET_PQ_get_oid_by_name (meta->db,
     835             :                                                     "int8",
     836             :                                                     &oid_v));
     837        7037 :           GNUNET_assert (GNUNET_OK ==
     838             :                          GNUNET_PQ_get_oid_by_name (meta->db,
     839             :                                                     "int4",
     840             :                                                     &oid_f));
     841             :           {
     842             :             struct TALER_PQ_AmountP am
     843        7037 :               = TALER_PQ_make_taler_pq_amount_ (
     844        7037 :                   &amounts[i],
     845             :                   oid_v,
     846             :                   oid_f);
     847             : 
     848        7037 :             GNUNET_memcpy (out,
     849             :                            &am,
     850             :                            sizeof(am));
     851             :           }
     852        7037 :           break;
     853             :         }
     854           0 :       case TALER_PQ_array_of_amount_currency:
     855             :         {
     856           0 :           const struct TALER_Amount *amounts = data;
     857             :           Oid oid_v;
     858             :           Oid oid_f;
     859             :           Oid oid_c;
     860             : 
     861             :           /* hoist out of loop? */
     862           0 :           GNUNET_assert (GNUNET_OK ==
     863             :                          GNUNET_PQ_get_oid_by_name (meta->db,
     864             :                                                     "int8",
     865             :                                                     &oid_v));
     866           0 :           GNUNET_assert (GNUNET_OK ==
     867             :                          GNUNET_PQ_get_oid_by_name (meta->db,
     868             :                                                     "int4",
     869             :                                                     &oid_f));
     870           0 :           GNUNET_assert (GNUNET_OK ==
     871             :                          GNUNET_PQ_get_oid_by_name (meta->db,
     872             :                                                     "varchar",
     873             :                                                     &oid_c));
     874             :           {
     875             :             struct TALER_PQ_AmountCurrencyP am;
     876             :             size_t len;
     877             : 
     878           0 :             len = TALER_PQ_make_taler_pq_amount_currency_ (
     879           0 :               &amounts[i],
     880             :               oid_v,
     881             :               oid_f,
     882             :               oid_c,
     883             :               &am);
     884           0 :             GNUNET_memcpy (out,
     885             :                            &am,
     886             :                            len);
     887             :           }
     888           0 :           break;
     889             :         }
     890         403 :       case TALER_PQ_array_of_blinded_denom_sig:
     891             :         {
     892         403 :           const struct TALER_BlindedDenominationSignature *denom_sigs = data;
     893         403 :           const struct GNUNET_CRYPTO_BlindedSignature *bs =
     894         403 :             denom_sigs[i].blinded_sig;
     895             :           uint32_t be[2];
     896             : 
     897         403 :           be[0] = htonl ((uint32_t) bs->cipher);
     898         403 :           be[1] = htonl (0x01);     /* magic margker: blinded */
     899         403 :           GNUNET_memcpy (out,
     900             :                          &be,
     901             :                          sizeof(be));
     902         403 :           out += sizeof(be);
     903         403 :           sz -= sizeof(be);
     904             : 
     905         403 :           switch (bs->cipher)
     906             :           {
     907         306 :           case GNUNET_CRYPTO_BSA_RSA:
     908             :             /* For RSA, 'same_sized' must have been false */
     909         306 :             GNUNET_assert (NULL != buffers);
     910         306 :             GNUNET_memcpy (out,
     911             :                            buffers[i],
     912             :                            sz);
     913         306 :             break;
     914          97 :           case GNUNET_CRYPTO_BSA_CS:
     915          97 :             GNUNET_memcpy (out,
     916             :                            &bs->details.blinded_cs_answer,
     917             :                            sz);
     918          97 :             break;
     919           0 :           default:
     920           0 :             GNUNET_assert (0);
     921             :           }
     922         403 :           break;
     923             :         }
     924           0 :       case TALER_PQ_array_of_blinded_coin_hash:
     925             :         {
     926           0 :           const struct TALER_BlindedCoinHashP *coin_hs = data;
     927             : 
     928           0 :           GNUNET_memcpy (out,
     929             :                          &coin_hs[i],
     930             :                          sizeof(struct TALER_BlindedCoinHashP));
     931             : 
     932           0 :           break;
     933             :         }
     934           0 :       case TALER_PQ_array_of_denom_hash:
     935             :         {
     936           0 :           const struct TALER_DenominationHashP *denom_hs = data;
     937             : 
     938           0 :           GNUNET_memcpy (out,
     939             :                          &denom_hs[i],
     940             :                          sizeof(struct TALER_DenominationHashP));
     941           0 :           break;
     942             :         }
     943           2 :       case TALER_PQ_array_of_hash_code:
     944             :         {
     945           2 :           const struct GNUNET_HashCode *hashes = data;
     946             : 
     947           2 :           GNUNET_memcpy (out,
     948             :                          &hashes[i],
     949             :                          sizeof(struct GNUNET_HashCode));
     950           2 :           break;
     951             :         }
     952         102 :       case TALER_PQ_array_of_cs_r_pub:
     953             :         {
     954         102 :           const struct GNUNET_CRYPTO_CSPublicRPairP *cs_r_pubs = data;
     955             : 
     956         102 :           GNUNET_memcpy (out,
     957             :                          &cs_r_pubs[i],
     958             :                          sizeof(struct GNUNET_CRYPTO_CSPublicRPairP));
     959         102 :           break;
     960             :         }
     961           0 :       default:
     962             :         {
     963           0 :           GNUNET_assert (0);
     964             :           break;
     965             :         }
     966             :       }
     967        7544 :       out += sz;
     968             :     }
     969             :   }
     970        1240 :   param_values[0] = elements;
     971        1240 :   param_lengths[0] = total_size;
     972        1240 :   param_formats[0] = 1;
     973        1240 :   scratch[0] = elements;
     974             : 
     975        1240 : DONE:
     976        1240 :   if (NULL != buffers)
     977             :   {
     978         521 :     for (size_t i = 0; i<num; i++)
     979         403 :       GNUNET_free (buffers[i]);
     980         118 :     GNUNET_free (buffers);
     981             :   }
     982        1240 :   GNUNET_free (buffer_lengths);
     983        1240 :   if (noerror)
     984        1240 :     return 1;
     985           0 :   return -1;
     986             : }
     987             : 
     988             : 
     989             : /**
     990             :  * Function to generate a typ specific query parameter and corresponding closure
     991             :  *
     992             :  * @param num Number of elements in @a elements
     993             :  * @param continuous If true, @a elements is an continuous array of data
     994             :  * @param elements Array of @a num elements, either continuous or pointers
     995             :  * @param sizes Array of @a num sizes, one per element, may be NULL
     996             :  * @param same_size If not 0, all elements in @a elements have this size
     997             :  * @param typ Supported internal type of each element in @a elements
     998             :  * @param oid Oid of the type to be used in Postgres
     999             :  * @param[in,out] db our database handle for looking up OIDs
    1000             :  * @return Query parameter
    1001             :  */
    1002             : static struct GNUNET_PQ_QueryParam
    1003        1240 : query_param_array_generic (
    1004             :   unsigned int num,
    1005             :   bool continuous,
    1006             :   const void *elements,
    1007             :   const size_t *sizes,
    1008             :   size_t same_size,
    1009             :   enum TALER_PQ_ArrayType typ,
    1010             :   Oid oid,
    1011             :   struct GNUNET_PQ_Context *db)
    1012             : {
    1013        1240 :   struct qconv_array_cls *meta = GNUNET_new (struct qconv_array_cls);
    1014             : 
    1015        1240 :   meta->typ = typ;
    1016        1240 :   meta->oid = oid;
    1017        1240 :   meta->sizes = sizes;
    1018        1240 :   meta->same_size = same_size;
    1019        1240 :   meta->continuous = continuous;
    1020        1240 :   meta->db = db;
    1021             : 
    1022             :   {
    1023        1240 :     struct GNUNET_PQ_QueryParam res = {
    1024             :       .conv = qconv_array,
    1025             :       .conv_cls = meta,
    1026             :       .conv_cls_cleanup = qconv_array_cls_cleanup,
    1027             :       .data = elements,
    1028             :       .size = num,
    1029             :       .num_params = 1,
    1030             :     };
    1031             : 
    1032        1240 :     return res;
    1033             :   }
    1034             : }
    1035             : 
    1036             : 
    1037             : struct GNUNET_PQ_QueryParam
    1038         118 : TALER_PQ_query_param_array_blinded_denom_sig (
    1039             :   size_t num,
    1040             :   const struct TALER_BlindedDenominationSignature *denom_sigs,
    1041             :   struct GNUNET_PQ_Context *db)
    1042             : {
    1043             :   Oid oid;
    1044             : 
    1045         118 :   GNUNET_assert (GNUNET_OK ==
    1046             :                  GNUNET_PQ_get_oid_by_name (db,
    1047             :                                             "bytea",
    1048             :                                             &oid));
    1049         118 :   return query_param_array_generic (num,
    1050             :                                     true,
    1051             :                                     denom_sigs,
    1052             :                                     NULL,
    1053             :                                     0,
    1054             :                                     TALER_PQ_array_of_blinded_denom_sig,
    1055             :                                     oid,
    1056             :                                     NULL);
    1057             : }
    1058             : 
    1059             : 
    1060             : struct GNUNET_PQ_QueryParam
    1061           0 : TALER_PQ_query_param_array_blinded_coin_hash (
    1062             :   size_t num,
    1063             :   const struct TALER_BlindedCoinHashP *coin_hs,
    1064             :   struct GNUNET_PQ_Context *db)
    1065             : {
    1066             :   Oid oid;
    1067             : 
    1068           0 :   GNUNET_assert (GNUNET_OK ==
    1069             :                  GNUNET_PQ_get_oid_by_name (db,
    1070             :                                             "bytea",
    1071             :                                             &oid));
    1072           0 :   return query_param_array_generic (num,
    1073             :                                     true,
    1074             :                                     coin_hs,
    1075             :                                     NULL,
    1076             :                                     sizeof(struct TALER_BlindedCoinHashP),
    1077             :                                     TALER_PQ_array_of_blinded_coin_hash,
    1078             :                                     oid,
    1079             :                                     NULL);
    1080             : }
    1081             : 
    1082             : 
    1083             : struct GNUNET_PQ_QueryParam
    1084           0 : TALER_PQ_query_param_array_denom_hash (
    1085             :   size_t num,
    1086             :   const struct TALER_DenominationHashP *denom_hs,
    1087             :   struct GNUNET_PQ_Context *db)
    1088             : {
    1089             :   Oid oid;
    1090             : 
    1091           0 :   GNUNET_assert (GNUNET_OK ==
    1092             :                  GNUNET_PQ_get_oid_by_name (db,
    1093             :                                             "bytea",
    1094             :                                             &oid));
    1095           0 :   return query_param_array_generic (num,
    1096             :                                     true,
    1097             :                                     denom_hs,
    1098             :                                     NULL,
    1099             :                                     sizeof(struct TALER_DenominationHashP),
    1100             :                                     TALER_PQ_array_of_denom_hash,
    1101             :                                     oid,
    1102             :                                     NULL);
    1103             : }
    1104             : 
    1105             : 
    1106             : struct GNUNET_PQ_QueryParam
    1107           1 : TALER_PQ_query_param_array_hash_code (
    1108             :   size_t num,
    1109             :   const struct GNUNET_HashCode *hashes,
    1110             :   struct GNUNET_PQ_Context *db)
    1111             : {
    1112             :   Oid oid;
    1113           1 :   GNUNET_assert (GNUNET_OK ==
    1114             :                  GNUNET_PQ_get_oid_by_name (db, "gnunet_hashcode", &oid));
    1115           1 :   return query_param_array_generic (num,
    1116             :                                     true,
    1117             :                                     hashes,
    1118             :                                     NULL,
    1119             :                                     sizeof(struct GNUNET_HashCode),
    1120             :                                     TALER_PQ_array_of_hash_code,
    1121             :                                     oid,
    1122             :                                     NULL);
    1123             : }
    1124             : 
    1125             : 
    1126             : struct GNUNET_PQ_QueryParam
    1127        1071 : TALER_PQ_query_param_array_amount (
    1128             :   size_t num,
    1129             :   const struct TALER_Amount *amounts,
    1130             :   struct GNUNET_PQ_Context *db)
    1131             : {
    1132             :   Oid oid;
    1133             : 
    1134        1071 :   GNUNET_assert (GNUNET_OK ==
    1135             :                  GNUNET_PQ_get_oid_by_name (db,
    1136             :                                             "taler_amount",
    1137             :                                             &oid));
    1138        1071 :   return query_param_array_generic (
    1139             :     num,
    1140             :     true,
    1141             :     amounts,
    1142             :     NULL,
    1143             :     sizeof(struct TALER_PQ_AmountP),
    1144             :     TALER_PQ_array_of_amount,
    1145             :     oid,
    1146             :     db);
    1147             : }
    1148             : 
    1149             : 
    1150             : struct GNUNET_PQ_QueryParam
    1151           0 : TALER_PQ_query_param_array_amount_with_currency (
    1152             :   size_t num,
    1153             :   const struct TALER_Amount *amounts,
    1154             :   struct GNUNET_PQ_Context *db)
    1155             : {
    1156             :   Oid oid;
    1157             : 
    1158           0 :   GNUNET_assert (GNUNET_OK ==
    1159             :                  GNUNET_PQ_get_oid_by_name (db,
    1160             :                                             "taler_amount_currency",
    1161             :                                             &oid));
    1162           0 :   return query_param_array_generic (
    1163             :     num,
    1164             :     true,
    1165             :     amounts,
    1166             :     NULL,
    1167             :     0, /* currency is technically variable length */
    1168             :     TALER_PQ_array_of_amount_currency,
    1169             :     oid,
    1170             :     db);
    1171             : }
    1172             : 
    1173             : 
    1174             : struct GNUNET_PQ_QueryParam
    1175          50 : TALER_PQ_query_param_array_cs_r_pub (
    1176             :   size_t num,
    1177             :   const struct GNUNET_CRYPTO_CSPublicRPairP *cs_r_pubs,
    1178             :   struct GNUNET_PQ_Context *db)
    1179             : {
    1180             :   Oid oid;
    1181             : 
    1182          50 :   GNUNET_assert (GNUNET_OK ==
    1183             :                  GNUNET_PQ_get_oid_by_name (db,
    1184             :                                             "bytea",
    1185             :                                             &oid));
    1186          50 :   return query_param_array_generic (
    1187             :     num,
    1188             :     true,
    1189             :     cs_r_pubs,
    1190             :     NULL,
    1191             :     sizeof(struct GNUNET_CRYPTO_CSPublicRPairP),
    1192             :     TALER_PQ_array_of_cs_r_pub,
    1193             :     oid,
    1194             :     db);
    1195             : }
    1196             : 
    1197             : 
    1198             : /* end of pq/pq_query_helper.c */

Generated by: LCOV version 1.16