/*
  This file is part of TALER
  Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
  TALER is free software; you can redistribute it and/or modify it under the
  terms of the GNU General Public License as published by the Free Software
  Foundation; either version 3, or (at your option) any later version.
  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  You should have received a copy of the GNU General Public License along with
  TALER; see the file COPYING.  If not, If not, see 
*/
/**
 * @file pq/db_pq.c
 * @brief helper functions for libpq (PostGres) interactions
 * @author Sree Harsha Totakura 
 * @author Florian Dold
 * @author Christian Grothoff
 */
#include "platform.h"
#include 
#include "taler_pq_lib.h"
/**
 * Execute a prepared statement.
 *
 * @param db_conn database connection
 * @param name name of the prepared statement
 * @param params parameters to the statement
 * @return postgres result
 */
PGresult *
TALER_PQ_exec_prepared (PGconn *db_conn,
                        const char *name,
                        const struct TALER_PQ_QueryParam *params)
{
  unsigned int len;
  unsigned int i;
  /* count the number of parameters */
  i = 0;
  len = 0;
  while (TALER_PQ_QF_END != params[i].format)
  {
    const struct TALER_PQ_QueryParam *x = ¶ms[i];
    switch (x->format)
    {
    case TALER_PQ_QF_FIXED_BLOB:
    case TALER_PQ_QF_VARSIZE_BLOB:
      len++;
      break;
    case TALER_PQ_QF_AMOUNT_NBO:
    case TALER_PQ_QF_AMOUNT:
      len += 3;
      break;
    case TALER_PQ_QF_RSA_PUBLIC_KEY:
    case TALER_PQ_QF_RSA_SIGNATURE:
    case TALER_PQ_QF_TIME_ABSOLUTE:
    case TALER_PQ_QF_UINT16:
    case TALER_PQ_QF_UINT32:
    case TALER_PQ_QF_UINT64:
    case TALER_PQ_QF_JSON:
      len++;
      break;
    default:
      /* format not supported */
      GNUNET_assert (0);
      break;
    }
    i++;
  }
  /* new scope to allow stack allocation without alloca */
  {
    /* Scratch buffer for temporary storage */
    void *scratch[len];
    /* Parameter array we are building for the query */
    void *param_values[len];
    int param_lengths[len];
    int param_formats[len];
    unsigned int off;
    /* How many entries in the scratch buffer are in use? */
    unsigned int soff;
    PGresult *res;
    i = 0;
    off = 0;
    soff = 0;
    while (TALER_PQ_QF_END != params[i].format)
    {
      const struct TALER_PQ_QueryParam *x = ¶ms[i];
      switch (x->format)
      {
      case TALER_PQ_QF_FIXED_BLOB:
      case TALER_PQ_QF_VARSIZE_BLOB:
        param_values[off] = (void *) x->data;
        param_lengths[off] = x->size;
        param_formats[off] = 1;
        off++;
        break;
      case TALER_PQ_QF_AMOUNT_NBO:
        {
          const struct TALER_Amount *amount = x->data;
          param_values[off] = (void *) &amount->value;
          param_lengths[off] = sizeof (amount->value);
          param_formats[off] = 1;
          off++;
          param_values[off] = (void *) &amount->fraction;
          param_lengths[off] = sizeof (amount->fraction);
          param_formats[off] = 1;
          off++;
          param_values[off] = (void *) amount->currency;
          param_lengths[off] = strlen (amount->currency);
          param_formats[off] = 1;
          off++;
        }
        break;
      case TALER_PQ_QF_AMOUNT:
        {
          const struct TALER_Amount *amount_hbo = x->data;
          struct TALER_AmountNBO *amount;
          amount = GNUNET_new (struct TALER_AmountNBO);
          scratch[soff++] = amount;
          TALER_amount_hton (amount,
                             amount_hbo);
          param_values[off] = (void *) &amount->value;
          param_lengths[off] = sizeof (amount->value);
          param_formats[off] = 1;
          off++;
          param_values[off] = (void *) &amount->fraction;
          param_lengths[off] = sizeof (amount->fraction);
          param_formats[off] = 1;
          off++;
          param_values[off] = (void *) amount->currency;
          param_lengths[off] = strlen (amount->currency);
          param_formats[off] = 1;
          off++;
        }
        break;
      case TALER_PQ_QF_RSA_PUBLIC_KEY:
        {
          const struct GNUNET_CRYPTO_rsa_PublicKey *rsa = x->data;
          char *buf;
          size_t buf_size;
          buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rsa,
                                                          &buf);
          scratch[soff++] = buf;
          param_values[off] = (void *) buf;
          param_lengths[off] = buf_size - 1; /* DB doesn't like the trailing \0 */
          param_formats[off] = 1;
          off++;
        }
        break;
      case TALER_PQ_QF_RSA_SIGNATURE:
        {
          const struct GNUNET_CRYPTO_rsa_Signature *sig = x->data;
          char *buf;
          size_t buf_size;
          buf_size = GNUNET_CRYPTO_rsa_signature_encode (sig,
                                                         &buf);
          scratch[soff++] = buf;
          param_values[off] = (void *) buf;
          param_lengths[off] = buf_size - 1; /* DB doesn't like the trailing \0 */
          param_formats[off] = 1;
          off++;
        }
        break;
      case TALER_PQ_QF_TIME_ABSOLUTE:
        {
          const struct GNUNET_TIME_Absolute *at_hbo = x->data;
          struct GNUNET_TIME_AbsoluteNBO *at_nbo;
          at_nbo = GNUNET_new (struct GNUNET_TIME_AbsoluteNBO);
          scratch[soff++] = at_nbo;
          *at_nbo = GNUNET_TIME_absolute_hton (*at_hbo);
          param_values[off] = (void *) at_nbo;
          param_lengths[off] = sizeof (struct GNUNET_TIME_AbsoluteNBO);
          param_formats[off] = 1;
          off++;
        }
        break;
      case TALER_PQ_QF_UINT16:
        {
          const uint16_t *u_hbo = x->data;
          uint16_t *u_nbo;
          u_nbo = GNUNET_new (uint16_t);
          scratch[soff++] = u_nbo;
          *u_nbo = htons (*u_hbo);
          param_values[off] = (void *) u_nbo;
          param_lengths[off] = sizeof (uint16_t);
          param_formats[off] = 1;
          off++;
        }
        break;
      case TALER_PQ_QF_UINT32:
        {
          const uint32_t *u_hbo = x->data;
          uint32_t *u_nbo;
          u_nbo = GNUNET_new (uint32_t);
          scratch[soff++] = u_nbo;
          *u_nbo = htonl (*u_hbo);
          param_values[off] = (void *) u_nbo;
          param_lengths[off] = sizeof (uint32_t);
          param_formats[off] = 1;
          off++;
        }
        break;
      case TALER_PQ_QF_UINT64:
        {
          const uint64_t *u_hbo = x->data;
          uint64_t *u_nbo;
          u_nbo = GNUNET_new (uint64_t);
          scratch[soff++] = u_nbo;
          *u_nbo = GNUNET_htonll (*u_hbo);
          param_values[off] = (void *) u_nbo;
          param_lengths[off] = sizeof (uint64_t);
          param_formats[off] = 1;
          off++;
        }
        break;
      case TALER_PQ_QF_JSON:
        {
          const json_t *json = x->data;
          char *str;
          str = json_dumps (json, JSON_COMPACT);
          GNUNET_assert (NULL != str);
          scratch[soff++] = str;
          param_values[off] = (void *) str;
          param_lengths[off] = strlen (str);
          param_formats[off] = 1;
          off++;
        }
        break;
      default:
        /* format not supported */
        GNUNET_assert (0);
        break;
      }
      i++;
    }
    GNUNET_assert (off == len);
    res = PQexecPrepared (db_conn,
                          name,
                          len,
                          (const char **) param_values,
                          param_lengths,
                          param_formats,
                          1);
    for (off = 0; off < soff; off++)
      GNUNET_free (scratch[off]);
    return res;
  }
}
/**
 * Free all memory that was allocated in @a rs during
 * #TALER_PQ_extract_result().
 *
 * @param rs reult specification to clean up
 */
void
TALER_PQ_cleanup_result (struct TALER_PQ_ResultSpec *rs)
{
  unsigned int i;
  for (i=0; TALER_PQ_RF_END != rs[i].format; i++)
  {
    switch (rs[i].format)
    {
    case TALER_PQ_RF_VARSIZE_BLOB:
      {
	void **dst = rs[i].dst;
	if (NULL != *dst)
	{
	  GNUNET_free (*dst);
	  *dst = NULL;
	  *rs[i].result_size = 0;
	}
	break;
      }
    case TALER_PQ_RF_RSA_PUBLIC_KEY:
      {
	void **dst = rs[i].dst;
	if (NULL != *dst)
	{
	  GNUNET_CRYPTO_rsa_public_key_free (*dst);
	  *dst = NULL;
	}
	break;
      }
    case TALER_PQ_RF_RSA_SIGNATURE:
      {
	void **dst = rs[i].dst;
	if (NULL != *dst)
	{
	  GNUNET_CRYPTO_rsa_signature_free (*dst);
	  *dst = NULL;
	}
      }
      break;
    default:
      break;
    }
  }
}
/**
 * Extract results from a query result according to the given specification.
 * If colums are NULL, the destination is not modified, and #GNUNET_NO
 * is returned.
 *
 * @param result result to process
 * @param[in,out] rs result specification to extract for
 * @param row row from the result to extract
 * @return
 *   #GNUNET_YES if all results could be extracted
 *   #GNUNET_NO if at least one result was NULL
 *   #GNUNET_SYSERR if a result was invalid (non-existing field)
 */
int
TALER_PQ_extract_result (PGresult *result,
                         struct TALER_PQ_ResultSpec *rs,
                         int row)
{
  unsigned int i;
  int had_null = GNUNET_NO;
  for (i=0; TALER_PQ_RF_END != rs[i].format; i++)
  {
    struct TALER_PQ_ResultSpec *spec;
    spec = &rs[i];
    switch (spec->format)
    {
    case TALER_PQ_RF_FIXED_BLOB:
    case TALER_PQ_RF_VARSIZE_BLOB:
      {
        size_t len;
        const char *res;
        void *dst;
        int fnum;
        fnum = PQfnumber (result,
                          spec->fname);
        if (fnum < 0)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' does not exist in result\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        if (PQgetisnull (result,
                         row,
                         fnum))
        {
          had_null = GNUNET_YES;
          continue;
        }
        /* if a field is null, continue but
         * remember that we now return a different result */
        len = PQgetlength (result,
                           row,
                           fnum);
        if ( (0 != spec->dst_size) &&
             (spec->dst_size != len) )
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' has wrong size (got %u, expected %u)\n",
                      spec->fname,
                      (unsigned int) len,
                      (unsigned int) spec->dst_size);
          TALER_PQ_cleanup_result (rs);
          return GNUNET_SYSERR;
        }
        res = PQgetvalue (result,
                          row,
                          fnum);
        GNUNET_assert (NULL != res);
        if (0 == spec->dst_size)
        {
          if (NULL != spec->result_size)
            *spec->result_size = len;
          spec->dst_size = len;
          dst = GNUNET_malloc (len);
          *((void **) spec->dst) = dst;
        }
        else
          dst = spec->dst;
        memcpy (dst,
                res,
                len);
        break;
      }
    case TALER_PQ_RF_AMOUNT_NBO:
      {
        char *val_name;
        char *frac_name;
        char *curr_name;
        const char *name = spec->fname;
        int ret;
        GNUNET_assert (NULL != spec->dst);
        GNUNET_assert (sizeof (struct TALER_AmountNBO) ==
                       spec->dst_size);
        GNUNET_asprintf (&val_name,
                         "%s_val",
                         name);
        GNUNET_asprintf (&frac_name,
                         "%s_frac",
                         name);
        GNUNET_asprintf (&curr_name,
                         "%s_curr",
                         name);
        ret = TALER_PQ_extract_amount_nbo (result,
                                           row,
                                           val_name,
                                           frac_name,
                                           curr_name,
                                           spec->dst);
        GNUNET_free (val_name);
        GNUNET_free (frac_name);
        GNUNET_free (curr_name);
        if (GNUNET_SYSERR == ret)
          return GNUNET_SYSERR;
        if (GNUNET_OK != ret)
          had_null = GNUNET_YES;
        break;
      }
    case TALER_PQ_RF_AMOUNT:
      {
        char *val_name;
        char *frac_name;
        char *curr_name;
        const char *name = spec->fname;
        int ret;
        GNUNET_assert (NULL != spec->dst);
        GNUNET_assert (sizeof (struct TALER_Amount) ==
                       spec->dst_size);
        GNUNET_asprintf (&val_name,
                         "%s_val",
                         name);
        GNUNET_asprintf (&frac_name,
                         "%s_frac",
                         name);
        GNUNET_asprintf (&curr_name,
                         "%s_curr",
                         name);
        ret = TALER_PQ_extract_amount (result,
                                       row,
                                       val_name,
                                       frac_name,
                                       curr_name,
                                       spec->dst);
        GNUNET_free (val_name);
        GNUNET_free (frac_name);
        GNUNET_free (curr_name);
        if (GNUNET_SYSERR == ret)
          return GNUNET_SYSERR;
        if (GNUNET_OK != ret)
          had_null = GNUNET_YES;
        break;
      }
    case TALER_PQ_RF_RSA_PUBLIC_KEY:
      {
        struct GNUNET_CRYPTO_rsa_PublicKey **pk = spec->dst;
        size_t len;
        const char *res;
        int fnum;
	*pk = NULL;
        fnum = PQfnumber (result,
                          spec->fname);
        if (fnum < 0)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' does not exist in result\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        if (PQgetisnull (result,
                         row,
                         fnum))
        {
          had_null = GNUNET_YES;
          continue;
        }
        /* if a field is null, continue but
         * remember that we now return a different result */
        len = PQgetlength (result,
                           row,
                           fnum);
        res = PQgetvalue (result,
                          row,
                          fnum);
        *pk = GNUNET_CRYPTO_rsa_public_key_decode (res,
                                                   len);
        if (NULL == *pk)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' contains bogus value (fails to decode)\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        break;
      }
    case TALER_PQ_RF_RSA_SIGNATURE:
      {
        struct GNUNET_CRYPTO_rsa_Signature **sig = spec->dst;
        size_t len;
        const char *res;
        int fnum;
	*sig = NULL;
        fnum = PQfnumber (result,
                          spec->fname);
        if (fnum < 0)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' does not exist in result\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        if (PQgetisnull (result,
                         row,
                         fnum))
        {
          had_null = GNUNET_YES;
          continue;
        }
        /* if a field is null, continue but
         * remember that we now return a different result */
        len = PQgetlength (result,
                           row,
                           fnum);
        res = PQgetvalue (result,
                          row,
                          fnum);
        *sig = GNUNET_CRYPTO_rsa_signature_decode (res,
                                                   len);
        if (NULL == *sig)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' contains bogus value (fails to decode)\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        break;
      }
    case TALER_PQ_RF_TIME_ABSOLUTE:
      {
        struct GNUNET_TIME_Absolute *dst = spec->dst;
	const struct GNUNET_TIME_AbsoluteNBO *res;
	int fnum;
        fnum = PQfnumber (result,
                          spec->fname);
        if (fnum < 0)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' does not exist in result\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        if (PQgetisnull (result,
                         row,
                         fnum))
        {
          had_null = GNUNET_YES;
          continue;
        }
        GNUNET_assert (NULL != dst);
        GNUNET_assert (sizeof (struct GNUNET_TIME_AbsoluteNBO) ==
                       spec->dst_size);
        res = (const struct GNUNET_TIME_AbsoluteNBO *)
	  PQgetvalue (result,
		      row,
		      fnum);
	*dst = GNUNET_TIME_absolute_ntoh (*res);
        break;
      }
    case TALER_PQ_RF_UINT16:
      {
        uint16_t *dst = spec->dst;
	const uint16_t *res;
	int fnum;
        fnum = PQfnumber (result,
                          spec->fname);
        if (fnum < 0)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' does not exist in result\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        if (PQgetisnull (result,
                         row,
                         fnum))
        {
          had_null = GNUNET_YES;
          continue;
        }
        GNUNET_assert (NULL != dst);
        GNUNET_assert (sizeof (uint16_t) ==
                       spec->dst_size);
        res = (uint16_t *) PQgetvalue (result,
                                       row,
                                       fnum);
	*dst = ntohs (*res);
        break;
      }
    case TALER_PQ_RF_UINT32:
      {
        uint32_t *dst = spec->dst;
	const uint32_t *res;
	int fnum;
        fnum = PQfnumber (result,
                          spec->fname);
        if (fnum < 0)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' does not exist in result\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        if (PQgetisnull (result,
                         row,
                         fnum))
        {
          had_null = GNUNET_YES;
          continue;
        }
        GNUNET_assert (NULL != dst);
        GNUNET_assert (sizeof (uint32_t) ==
                       spec->dst_size);
        res = (uint32_t *) PQgetvalue (result,
                                       row,
                                       fnum);
	*dst = ntohl (*res);
        break;
      }
    case TALER_PQ_RF_UINT64:
      {
        uint64_t *dst = spec->dst;
	const uint64_t *res;
	int fnum;
        fnum = PQfnumber (result,
                          spec->fname);
        if (fnum < 0)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' does not exist in result\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        if (PQgetisnull (result,
                         row,
                         fnum))
        {
          had_null = GNUNET_YES;
          continue;
        }
        GNUNET_assert (NULL != dst);
        GNUNET_assert (sizeof (uint64_t) ==
                       spec->dst_size);
        res = (uint64_t *) PQgetvalue (result,
                                       row,
                                       fnum);
	*dst = GNUNET_ntohll (*res);
        break;
      }
    case TALER_PQ_RF_JSON:
      {
        json_t **dst = spec->dst;
        char *res;
	int fnum;
        json_error_t json_error;
        size_t slen;
        fnum = PQfnumber (result,
                          spec->fname);
        if (fnum < 0)
        {
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Field `%s' does not exist in result\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        if (PQgetisnull (result,
                         row,
                         fnum))
        {
          had_null = GNUNET_YES;
          continue;
        }
        GNUNET_assert (NULL != dst);
        GNUNET_break (0 == spec->dst_size);
        slen = PQgetlength (result,
                            row,
                            fnum);
        res = (char *) PQgetvalue (result,
                                   row,
                                   fnum);
	*dst = json_loadb (res,
                           slen,
                           JSON_REJECT_DUPLICATES,
                           &json_error);
        if (NULL == *dst)
        {
          TALER_json_warn (json_error);
          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                      "Failed to parse JSON result for field `%s'\n",
                      spec->fname);
          return GNUNET_SYSERR;
        }
        break;
      }
    default:
      GNUNET_assert (0);
      break;
    }
  }
  if (GNUNET_YES == had_null)
    return GNUNET_NO;
  return GNUNET_YES;
}
/**
 * Extract a currency amount from a query result according to the
 * given specification.
 *
 * @param result the result to extract the amount from
 * @param row which row of the result to extract the amount from (needed as results can have multiple rows)
 * @param val_name name of the column with the amount's "value", must include the substring "_val".
 * @param frac_name name of the column with the amount's "fractional" value, must include the substring "_frac".
 * @param curr_name name of the column with the amount's currency name, must include the substring "_curr".
 * @param[out] r_amount_nbo where to store the amount, in network byte order
 * @return
 *   #GNUNET_YES if all results could be extracted
 *   #GNUNET_NO if at least one result was NULL
 *   #GNUNET_SYSERR if a result was invalid (non-existing field)
 */
int
TALER_PQ_extract_amount_nbo (PGresult *result,
                             int row,
                             const char *val_name,
                             const char *frac_name,
                             const char *curr_name,
                             struct TALER_AmountNBO *r_amount_nbo)
{
  int val_num;
  int frac_num;
  int curr_num;
  int len;
  /* These checks are simply to check that clients obey by our naming
     conventions, and not for any functional reason */
  GNUNET_assert (NULL !=
                 strstr (val_name,
                         "_val"));
  GNUNET_assert (NULL !=
                 strstr (frac_name,
                         "_frac"));
  GNUNET_assert (NULL !=
                 strstr (curr_name,
                         "_curr"));
  /* Set return value to invalid in case we don't finish */
  memset (r_amount_nbo,
          0,
          sizeof (struct TALER_AmountNBO));
  val_num = PQfnumber (result,
                       val_name);
  frac_num = PQfnumber (result,
                        frac_name);
  curr_num = PQfnumber (result,
                        curr_name);
  if ( (val_num < 0) ||
       (frac_num < 0) ||
       (curr_num < 0) )
  {
    GNUNET_break (0);
    return GNUNET_SYSERR;
  }
  if ( (PQgetisnull (result,
                     row,
                     val_num)) ||
       (PQgetisnull (result,
                     row,
                     frac_num)) ||
       (PQgetisnull (result,
                     row,
                     curr_num)) )
  {
    GNUNET_break (0);
    return GNUNET_NO;
  }
  /* Note that Postgres stores value in NBO internally,
     so no conversion needed in this case */
  r_amount_nbo->value = *(uint64_t *) PQgetvalue (result,
                                                  row,
                                                  val_num);
  r_amount_nbo->fraction = *(uint32_t *) PQgetvalue (result,
                                                     row,
                                                     frac_num);
  len = GNUNET_MIN (TALER_CURRENCY_LEN - 1,
                    PQgetlength (result,
                                 row,
                                 curr_num));
  memcpy (r_amount_nbo->currency,
          PQgetvalue (result,
                      row,
                      curr_num),
          len);
  return GNUNET_OK;
}
/**
 * Extract a currency amount from a query result according to the
 * given specification.
 *
 * @param result the result to extract the amount from
 * @param row which row of the result to extract the amount from (needed as
 *          results can have multiple rows)
 * @param val_name name of the column with the amount's "value", must include
 *          the substring "_val".
 * @param frac_name name of the column with the amount's "fractional" value,
 *          must include the substring "_frac".
 * @param curr_name name of the column with the amount's currency name, must
 *          include the substring "_curr".
 * @param[out] r_amount where to store the amount, in host byte order
 * @return
 *   #GNUNET_YES if all results could be extracted
 *   #GNUNET_NO if at least one result was NULL
 *   #GNUNET_SYSERR if a result was invalid (non-existing field)
 */
int
TALER_PQ_extract_amount (PGresult *result,
                         int row,
                         const char *val_name,
                         const char *frac_name,
                         const char *curr_name,
                         struct TALER_Amount *r_amount)
{
  struct TALER_AmountNBO amount_nbo;
  int ret;
  ret = TALER_PQ_extract_amount_nbo (result,
                                     row,
                                     val_name,
                                     frac_name,
                                     curr_name,
                                     &amount_nbo);
  TALER_amount_ntoh (r_amount,
                     &amount_nbo);
  return ret;
}
/* end of pq/db_pq.c */