diff options
Diffstat (limited to 'src/pq')
| -rw-r--r-- | src/pq/Makefile.am | 8 | ||||
| -rw-r--r-- | src/pq/db_pq.c | 883 | ||||
| -rw-r--r-- | src/pq/pq_helper.c | 370 | ||||
| -rw-r--r-- | src/pq/pq_query_helper.c | 208 | ||||
| -rw-r--r-- | src/pq/pq_result_helper.c | 358 | ||||
| -rw-r--r-- | src/pq/test_pq.c | 153 | 
6 files changed, 595 insertions, 1385 deletions
| diff --git a/src/pq/Makefile.am b/src/pq/Makefile.am index 2187d494..810aa47b 100644 --- a/src/pq/Makefile.am +++ b/src/pq/Makefile.am @@ -12,8 +12,8 @@ lib_LTLIBRARIES = \    libtalerpq.la  libtalerpq_la_SOURCES = \ -  db_pq.c \ -  pq_helper.c +  pq_query_helper.c \ +  pq_result_helper.c  libtalerpq_la_LIBADD = \    $(top_builddir)/src/util/libtalerutil.la  \ @@ -36,5 +36,7 @@ test_pq_SOURCES = \  test_pq_LDADD = \    libtalerpq.la \    $(top_builddir)/src/util/libtalerutil.la  \ -  -lgnunetutil -ljansson \ +  -lgnunetpq \ +  -lgnunetutil \ +  -ljansson \    -lpq $(XLIB) diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c deleted file mode 100644 index 59a1db02..00000000 --- a/src/pq/db_pq.c +++ /dev/null @@ -1,883 +0,0 @@ -/* -  This file is part of TALER -  Copyright (C) 2014, 2015 GNUnet e.V. - -  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 <http://www.gnu.org/licenses/> -*/ -/** - * @file pq/db_pq.c - * @brief helper functions for libpq (PostGres) interactions - * @author Sree Harsha Totakura <sreeharsha@totakura.in> - * @author Florian Dold - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#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 */ diff --git a/src/pq/pq_helper.c b/src/pq/pq_helper.c deleted file mode 100644 index d245ffdb..00000000 --- a/src/pq/pq_helper.c +++ /dev/null @@ -1,370 +0,0 @@ -/* -  This file is part of TALER -  Copyright (C) 2014, 2015 GNUnet e.V. - -  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 <http://www.gnu.org/licenses/> -*/ -/** - * @file pq/pq_helper.c - * @brief functions to initialize parameter arrays - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include "taler_pq_lib.h" - - -/** - * Generate query parameter for a currency, consisting of the three - * components "value", "fraction" and "currency" in this order. The - * types must be a 64-bit integer, 32-bit integer and a - * #TALER_CURRENCY_LEN-sized BLOB/VARCHAR respectively. - * - * @param x pointer to the query parameter to pass - * @return array entry for the query parameters to use - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_amount_nbo (const struct TALER_AmountNBO *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_AMOUNT_NBO, x, sizeof (*x) }; -  return res; -} - - -/** - * Generate query parameter for a currency, consisting of the three - * components "value", "fraction" and "currency" in this order. The - * types must be a 64-bit integer, 32-bit integer and a - * #TALER_CURRENCY_LEN-sized BLOB/VARCHAR respectively. - * - * @param x pointer to the query parameter to pass - * @return array entry for the query parameters to use - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_amount (const struct TALER_Amount *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_AMOUNT, x, sizeof (*x) }; -  return res; -} - - -/** - * Generate query parameter for an RSA public key.  The - * database must contain a BLOB type in the respective position. - * - * @param x the query parameter to pass - * @return array entry for the query parameters to use - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_rsa_public_key (const struct GNUNET_CRYPTO_rsa_PublicKey *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_RSA_PUBLIC_KEY, (x), 0 }; -  return res; -} - - -/** - * Generate query parameter for an RSA signature.  The - * database must contain a BLOB type in the respective position. - * - * @param x the query parameter to pass - * @return array entry for the query parameters to use - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_rsa_Signature *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_RSA_SIGNATURE, (x), 0 }; -  return res; -} - - -/** - * Generate query parameter for an absolute time value. - * The database must store a 64-bit integer. - * - * @param x pointer to the query parameter to pass - * @return array entry for the query parameters to use - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_TIME_ABSOLUTE, x, sizeof (*x) }; -  return res; -} - - -/** - * Generate query parameter for an absolute time value. - * The database must store a 64-bit integer. - * - * @param x pointer to the query parameter to pass - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_absolute_time_nbo(const struct GNUNET_TIME_AbsoluteNBO *x) -{ -  struct TALER_PQ_QueryParam res = -    TALER_PQ_query_param_auto_from_type (&x->abs_value_us__); -  return res; -} - - -/** - * Generate query parameter for an uint16_t in host byte order. - * - * @param x pointer to the query parameter to pass - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_uint16 (const uint16_t *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_UINT16, x, sizeof (*x) }; -  return res; -} - - -/** - * Generate query parameter for an uint32_t in host byte order. - * - * @param x pointer to the query parameter to pass - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_uint32 (const uint32_t *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_UINT32, x, sizeof (*x) }; -  return res; -} - - -/** - * Generate query parameter for an uint16_t in host byte order. - * - * @param x pointer to the query parameter to pass - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_uint64 (const uint64_t *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_UINT64, x, sizeof (*x) }; -  return res; -} - - -/** - * Generate query parameter for a JSON object (stored as a string - * in the DB). - * - * @param x pointer to the json object to pass - */ -struct TALER_PQ_QueryParam -TALER_PQ_query_param_json (const json_t *x) -{ -  struct TALER_PQ_QueryParam res = -    { TALER_PQ_QF_JSON, x, 0 }; -  return res; -} - - -/** - * Variable-size result expected. - * - * @param name name of the field in the table - * @param[out] dst where to store the result, allocated - * @param[out] sptr where to store the size of @a dst - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_variable_size (const char *name, -                                    void **dst, -                                    size_t *sptr) -{ -  struct TALER_PQ_ResultSpec res = -    { TALER_PQ_RF_VARSIZE_BLOB, (void *) (dst), 0, name, sptr }; -  return res; -} - - -/** - * Currency amount expected. - * - * @param name name of the field in the table - * @param[out] amount where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_amount_nbo (const char *name, -				 struct TALER_AmountNBO *amount) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_AMOUNT_NBO, (void *) amount, sizeof (*amount), name, NULL }; -  return res; -} - - -/** - * Currency amount expected. - * - * @param name name of the field in the table - * @param[out] amount where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_amount (const char *name, -			     struct TALER_Amount *amount) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_AMOUNT, (void *) amount, sizeof (*amount), name, NULL }; -  return res; -} - - -/** - * RSA public key expected. - * - * @param name name of the field in the table - * @param[out] rsa where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_rsa_public_key (const char *name, -				     struct GNUNET_CRYPTO_rsa_PublicKey **rsa) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_RSA_PUBLIC_KEY, (void *) rsa, 0, name, NULL }; -  return res; -} - - -/** - * RSA signature expected. - * - * @param name name of the field in the table - * @param[out] sig where to store the result; - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_rsa_signature (const char *name, -				    struct GNUNET_CRYPTO_rsa_Signature **sig) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_RSA_SIGNATURE, (void *) sig, 0, (name), NULL }; -  return res; -} - - -/** - * Absolute time expected. - * - * @param name name of the field in the table - * @param[out] at where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_absolute_time (const char *name, -				    struct GNUNET_TIME_Absolute *at) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_TIME_ABSOLUTE, (void *) at, sizeof (*at), (name), NULL }; -  return res; -} - - -/** - * Absolute time expected. - * - * @param name name of the field in the table - * @param[out] at where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_absolute_time_nbo (const char *name, -                                        struct GNUNET_TIME_AbsoluteNBO *at) -{ -  struct TALER_PQ_ResultSpec res = -    TALER_PQ_result_spec_auto_from_type(name, &at->abs_value_us__); -  return res; -} - - -/** - * uint16_t expected. - * - * @param name name of the field in the table - * @param[out] u16 where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_uint16 (const char *name, -                             uint16_t *u16) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_UINT16, (void *) u16, sizeof (*u16), (name), NULL }; -  return res; -} - - -/** - * uint32_t expected. - * - * @param name name of the field in the table - * @param[out] u32 where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_uint32 (const char *name, -                             uint32_t *u32) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_UINT32, (void *) u32, sizeof (*u32), (name), NULL }; -  return res; -} - - -/** - * uint64_t expected. - * - * @param name name of the field in the table - * @param[out] u64 where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_uint64 (const char *name, -                             uint64_t *u64) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_UINT64, (void *) u64, sizeof (*u64), (name), NULL }; -  return res; -} - - -/** - * json_t expected. - * - * @param name name of the field in the table - * @param[out] jp where to store the result - * @return array entry for the result specification to use - */ -struct TALER_PQ_ResultSpec -TALER_PQ_result_spec_json (const char *name, -                           json_t **jp) -{ -  struct TALER_PQ_ResultSpec res = -    {TALER_PQ_RF_JSON, (void *) jp, 0, (name), NULL }; -  return res; -} - -/* end of pq_helper.c */ diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c new file mode 100644 index 00000000..5d2e9ce0 --- /dev/null +++ b/src/pq/pq_query_helper.c @@ -0,0 +1,208 @@ +/* +  This file is part of TALER +  Copyright (C) 2014, 2015, 2016 GNUnet e.V. + +  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 <http://www.gnu.org/licenses/> +*/ +/** + * @file pq/pq_query_helper.c + * @brief helper functions for Taler-specific libpq (PostGres) interactions + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + * @author Florian Dold + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_pq_lib.h> +#include "taler_pq_lib.h" + + +/** + * Function called to convert input argument into SQL parameters. + * + * @param cls closure + * @param data pointer to input argument, here a `struct TALER_AmountNBO` + * @param data_len number of bytes in @a data (if applicable) + * @param[out] param_values SQL data to set + * @param[out] param_lengths SQL length data to set + * @param[out] param_formats SQL format data to set + * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays + * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() + * @param scratch_length number of entries left in @a scratch + * @return -1 on error, number of offsets used in @a scratch otherwise + */ +static int +qconv_amount_nbo (void *cls, +		  const void *data, +		  size_t data_len, +		  void *param_values[], +		  int param_lengths[], +		  int param_formats[], +		  unsigned int param_length, +		  void *scratch[], +		  unsigned int scratch_length) +{ +  const struct TALER_AmountNBO *amount = data; +  unsigned int off = 0; + +  GNUNET_assert (3 == param_length); +  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; +  return 0; +} + + +/** + * Generate query parameter for a currency, consisting of the three + * components "value", "fraction" and "currency" in this order. The + * types must be a 64-bit integer, 32-bit integer and a + * #TALER_CURRENCY_LEN-sized BLOB/VARCHAR respectively. + * + * @param x pointer to the query parameter to pass + * @return array entry for the query parameters to use + */ +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_amount_nbo (const struct TALER_AmountNBO *x) +{ +  struct GNUNET_PQ_QueryParam res = +    { &qconv_amount_nbo, NULL, x, sizeof (*x), 3 }; +  return res; +} + + +/** + * Function called to convert input argument into SQL parameters. + * + * @param cls closure + * @param data pointer to input argument, here a `struct TALER_Amount` + * @param data_len number of bytes in @a data (if applicable) + * @param[out] param_values SQL data to set + * @param[out] param_lengths SQL length data to set + * @param[out] param_formats SQL format data to set + * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays + * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() + * @param scratch_length number of entries left in @a scratch + * @return -1 on error, number of offsets used in @a scratch otherwise + */ +static int +qconv_amount (void *cls, +	      const void *data, +	      size_t data_len, +	      void *param_values[], +	      int param_lengths[], +	      int param_formats[], +	      unsigned int param_length, +	      void *scratch[], +	      unsigned int scratch_length) +{ +  const struct TALER_Amount *amount_hbo = data; +  struct TALER_AmountNBO *amount; + +  amount = GNUNET_new (struct TALER_AmountNBO); +  scratch[0] = amount; +  TALER_amount_hton (amount, +		     amount_hbo); +  qconv_amount_nbo (cls, +		    amount, +		    sizeof (struct TALER_AmountNBO), +		    param_values, +		    param_lengths, +		    param_formats, +		    param_length, +		    &scratch[1], +		    scratch_length - 1); +  return 1; +} + + +/** + * Generate query parameter for a currency, consisting of the three + * components "value", "fraction" and "currency" in this order. The + * types must be a 64-bit integer, 32-bit integer and a + * #TALER_CURRENCY_LEN-sized BLOB/VARCHAR respectively. + * + * @param x pointer to the query parameter to pass + * @return array entry for the query parameters to use + */ +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_amount (const struct TALER_Amount *x) +{ +  struct GNUNET_PQ_QueryParam res = +    { &qconv_amount, NULL, x, sizeof (*x), 3 }; +  return res; +} + + +/** + * Function called to convert input argument into SQL parameters. + * + * @param cls closure + * @param data pointer to input argument, here a `json_t *` + * @param data_len number of bytes in @a data (if applicable) + * @param[out] param_values SQL data to set + * @param[out] param_lengths SQL length data to set + * @param[out] param_formats SQL format data to set + * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays + * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() + * @param scratch_length number of entries left in @a scratch + * @return -1 on error, number of offsets used in @a scratch otherwise + */ +static int +qconv_json (void *cls, +	    const void *data, +	    size_t data_len, +	    void *param_values[], +	    int param_lengths[], +	    int param_formats[], +	    unsigned int param_length, +	    void *scratch[], +	    unsigned int scratch_length) +{ +  const json_t *json = data; +  char *str; +   +  str = json_dumps (json, JSON_COMPACT); +  if (NULL == str) +    return -1; +  scratch[0] = str; +  param_values[0] = (void *) str; +  param_lengths[0] = strlen (str); +  param_formats[0] = 1; +  return 1; +} + + +/** + * Generate query parameter for a JSON object (stored as a string + * in the DB). + * + * @param x pointer to the json object to pass + */ +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_json (const json_t *x) +{ +  struct GNUNET_PQ_QueryParam res = +    { &qconv_json, NULL, x, 0, 1 }; +  return res; +} + + +/* end of pq/pq_query_helper.c */ diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c new file mode 100644 index 00000000..c8b3b01f --- /dev/null +++ b/src/pq/pq_result_helper.c @@ -0,0 +1,358 @@ +/* +  This file is part of TALER +  Copyright (C) 2014, 2015, 2016 GNUnet e.V. + +  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 <http://www.gnu.org/licenses/> +*/ +/** + * @file pq/pq_result_helper.c + * @brief functions to initialize parameter arrays + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_pq_lib.h" + + +/** + * 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) + */ +static int +extract_amount_nbo_helper (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 data from a Postgres database @a result at row @a row. + * + * @param cls closure + * @param result where to extract data from + * @param int row to extract data from + * @param fname name (or prefix) of the fields to extract from + * @param[in,out] dst_size where to store size of result, may be NULL + * @param[out] dst where to store the result + * @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) + */  +static int +extract_amount_nbo (void *cls, +		    PGresult *result, +		    int row, +		    const char *fname, +		    size_t *dst_size, +		    void *dst) +{ +  char *val_name; +  char *frac_name; +  char *curr_name; +  int ret; +   +  GNUNET_asprintf (&val_name, +		   "%s_val", +		   fname); +  GNUNET_asprintf (&frac_name, +		   "%s_frac", +		   fname); +  GNUNET_asprintf (&curr_name, +		   "%s_curr", +		   fname); +  ret = extract_amount_nbo_helper (result, +				   row, +				   val_name, +				   frac_name, +				   curr_name, +				   dst); +  GNUNET_free (val_name); +  GNUNET_free (frac_name); +  GNUNET_free (curr_name); +  return ret; +} + + +/** + * Currency amount expected. + * + * @param name name of the field in the table + * @param[out] amount where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_amount_nbo (const char *name, +				 struct TALER_AmountNBO *amount) +{ +  struct GNUNET_PQ_ResultSpec res = +    { &extract_amount_nbo, NULL, NULL, +      (void *) amount, sizeof (*amount),  +      name, NULL }; +  return res; +} + + +/** + * Extract data from a Postgres database @a result at row @a row. + * + * @param cls closure + * @param result where to extract data from + * @param int row to extract data from + * @param fname name (or prefix) of the fields to extract from + * @param[in,out] dst_size where to store size of result, may be NULL + * @param[out] dst where to store the result + * @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) + */  +static int +extract_amount (void *cls, +		PGresult *result, +		int row, +		const char *fname, +		size_t *dst_size, +		void *dst) +{ +  struct TALER_Amount *r_amount = dst; +  char *val_name; +  char *frac_name; +  char *curr_name; +  struct TALER_AmountNBO amount_nbo; +  int ret; +   +  GNUNET_asprintf (&val_name, +		   "%s_val", +		   fname); +  GNUNET_asprintf (&frac_name, +		   "%s_frac", +		   fname); +  GNUNET_asprintf (&curr_name, +		   "%s_curr", +		   fname); +  ret = extract_amount_nbo_helper (result, +				   row, +				   val_name, +				   frac_name, +				   curr_name, +				   &amount_nbo); +  TALER_amount_ntoh (r_amount, +                     &amount_nbo); +  GNUNET_free (val_name); +  GNUNET_free (frac_name); +  GNUNET_free (curr_name); +  return ret; +} + + +/** + * Currency amount expected. + * + * @param name name of the field in the table + * @param[out] amount where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_amount (const char *name, +			     struct TALER_Amount *amount) +{ +  struct GNUNET_PQ_ResultSpec res = +    { &extract_amount, NULL, NULL, +      (void *) amount, sizeof (*amount), +      name, NULL }; +  return res; +} + + +/** + * Extract data from a Postgres database @a result at row @a row. + * + * @param cls closure + * @param result where to extract data from + * @param int row to extract data from + * @param fname name (or prefix) of the fields to extract from + * @param[in,out] dst_size where to store size of result, may be NULL + * @param[out] dst where to store the result + * @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) + */  +static int +extract_json (void *cls, +	      PGresult *result, +	      int row, +	      const char *fname, +	      size_t *dst_size, +	      void *dst) +{ +  json_t **j_dst = dst; +  const char *res; +  int fnum; +  json_error_t json_error; +  size_t slen; +   +  fnum = PQfnumber (result, +		    fname); +  if (fnum < 0) +  { +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +		"Field `%s' does not exist in result\n", +		fname); +    return GNUNET_SYSERR; +  } +  if (PQgetisnull (result, +		   row, +		   fnum)) +    return GNUNET_NO; +  slen = PQgetlength (result, +		      row, +		      fnum); +  res = (const char *) PQgetvalue (result, +				   row, +				   fnum); +  *j_dst = json_loadb (res, +		       slen, +		       JSON_REJECT_DUPLICATES, +		       &json_error); +  if (NULL == *j_dst) +  { +    TALER_json_warn (json_error); +    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +		"Failed to parse JSON result for field `%s'\n", +		fname); +    return GNUNET_SYSERR; +  } +  return GNUNET_OK; +} + + +/** + * Function called to clean up memory allocated + * by a #GNUNET_PQ_ResultConverter. + * + * @param cls closure + * @param rd result data to clean up + */ +static void +clean_json (void *cls, +	    void *rd) +{ +  json_t **dst = rd; + +  if (NULL != *dst) +  { +    json_decref (*dst); +    *dst = NULL; +  } +} + + +/** + * json_t expected. + * + * @param name name of the field in the table + * @param[out] jp where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_json (const char *name, +                           json_t **jp) +{ +  struct GNUNET_PQ_ResultSpec res = +    { &extract_json, &clean_json, NULL, +      (void *) jp, 0,  +      name, NULL }; +  return res; +} + +/* end of pq_result_helper.c */ diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c index f0413d1f..95cfb715 100644 --- a/src/pq/test_pq.c +++ b/src/pq/test_pq.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  (C) 2015 GNUnet e.V. +  (C) 2015, 2016 GNUnet e.V.    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 @@ -48,47 +48,27 @@ postgres_prepare (PGconn *db_conn)    PREPARE ("test_insert",             "INSERT INTO test_pq (" -           " pub" -           ",sig" -           ",abs_time" -           ",forever" -           ",hash" -           ",hamount_val" +           " hamount_val"             ",hamount_frac"             ",hamount_curr"             ",namount_val"             ",namount_frac"             ",namount_curr" -           ",vsize" -           ",u16" -           ",u32" -           ",u64"             ",json"             ") VALUES "             "($1, $2, $3, $4, $5, $6," -            "$7, $8, $9, $10, $11, $12, $13, $14, $15, $16);", -           16, NULL); +            "$7);", +           7, NULL);    PREPARE ("test_select",             "SELECT" -           " pub" -           ",sig" -           ",abs_time" -           ",forever" -           ",hash" -           ",hamount_val" +           " hamount_val"             ",hamount_frac"             ",hamount_curr"             ",namount_val"             ",namount_frac"             ",namount_curr" -	   ",vsize" -           ",u16" -           ",u32" -           ",u64"             ",json" -           " FROM test_pq" -           " ORDER BY abs_time DESC " -           " LIMIT 1;", +           " FROM test_pq;",             0, NULL);    return GNUNET_OK;  #undef PREPARE @@ -103,40 +83,15 @@ postgres_prepare (PGconn *db_conn)  static int  run_queries (PGconn *conn)  { -  struct GNUNET_CRYPTO_rsa_PublicKey *pub; -  struct GNUNET_CRYPTO_rsa_PublicKey *pub2 = NULL; -  struct GNUNET_CRYPTO_rsa_Signature *sig; -  struct GNUNET_CRYPTO_rsa_Signature *sig2 = NULL; -  struct GNUNET_TIME_Absolute abs_time = GNUNET_TIME_absolute_get (); -  struct GNUNET_TIME_Absolute abs_time2; -  struct GNUNET_TIME_Absolute forever = GNUNET_TIME_UNIT_FOREVER_ABS; -  struct GNUNET_TIME_Absolute forever2; -  struct GNUNET_HashCode hc; -  struct GNUNET_HashCode hc2;    struct TALER_Amount hamount;    struct TALER_Amount hamount2;    struct TALER_AmountNBO namount;    struct TALER_AmountNBO namount2;    PGresult *result;    int ret; -  struct GNUNET_CRYPTO_rsa_PrivateKey *priv; -  char msg[] = "Hello"; -  void *msg2; -  size_t msg2_len; -  uint16_t u16; -  uint16_t u162; -  uint32_t u32; -  uint32_t u322; -  uint64_t u64; -  uint64_t u642;    json_t *json;    json_t *json2; -  priv = GNUNET_CRYPTO_rsa_private_key_create (1024); -  pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv); -  sig = GNUNET_CRYPTO_rsa_sign (priv, -				msg, -				sizeof (msg));    GNUNET_assert (GNUNET_OK ==                   TALER_string_to_amount ("EUR:5.5",                                           &hamount)); @@ -145,87 +100,53 @@ run_queries (PGconn *conn)    GNUNET_assert (GNUNET_OK ==                   TALER_string_to_amount ("EUR:4.4",                                           &hamount)); -  u16 = 16; -  u32 = 32; -  u64 = 64;    json = json_object ();    json_object_set (json, "foo", json_integer (42));    GNUNET_assert (NULL != json); -  /* FIXME: test TALER_PQ_result_spec_variable_size */ +  /* FIXME: test GNUNET_PQ_result_spec_variable_size */    { -    struct TALER_PQ_QueryParam params_insert[] = { -      TALER_PQ_query_param_rsa_public_key (pub), -      TALER_PQ_query_param_rsa_signature (sig), -      TALER_PQ_query_param_absolute_time (&abs_time), -      TALER_PQ_query_param_absolute_time (&forever), -      TALER_PQ_query_param_auto_from_type (&hc), +    struct GNUNET_PQ_QueryParam params_insert[] = {        TALER_PQ_query_param_amount (&hamount),        TALER_PQ_query_param_amount_nbo (&namount), -      TALER_PQ_query_param_fixed_size (msg, strlen (msg)), -      TALER_PQ_query_param_uint16 (&u16), -      TALER_PQ_query_param_uint32 (&u32), -      TALER_PQ_query_param_uint64 (&u64),        TALER_PQ_query_param_json (json), -      TALER_PQ_query_param_end +      GNUNET_PQ_query_param_end      }; -    struct TALER_PQ_QueryParam params_select[] = { -      TALER_PQ_query_param_end +    struct GNUNET_PQ_QueryParam params_select[] = { +      GNUNET_PQ_query_param_end      }; -    struct TALER_PQ_ResultSpec results_select[] = { -      TALER_PQ_result_spec_rsa_public_key ("pub", &pub2), -      TALER_PQ_result_spec_rsa_signature ("sig", &sig2), -      TALER_PQ_result_spec_absolute_time ("abs_time", &abs_time2), -      TALER_PQ_result_spec_absolute_time ("forever", &forever2), -      TALER_PQ_result_spec_auto_from_type ("hash", &hc2), +    struct GNUNET_PQ_ResultSpec results_select[] = {        TALER_PQ_result_spec_amount ("hamount", &hamount2),        TALER_PQ_result_spec_amount_nbo ("namount", &namount2), -      TALER_PQ_result_spec_variable_size ("vsize", &msg2, &msg2_len), -      TALER_PQ_result_spec_uint16 ("u16", &u162), -      TALER_PQ_result_spec_uint32 ("u32", &u322), -      TALER_PQ_result_spec_uint64 ("u64", &u642),        TALER_PQ_result_spec_json ("json", &json2), -      TALER_PQ_result_spec_end +      GNUNET_PQ_result_spec_end      }; -    result = TALER_PQ_exec_prepared (conn, -				     "test_insert", -				     params_insert); +    result = GNUNET_PQ_exec_prepared (conn, +				      "test_insert", +				      params_insert);      if (PGRES_COMMAND_OK != PQresultStatus (result))      {        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,  		  "Database failure: %s\n",  		  PQresultErrorMessage (result));        PQclear (result); -      GNUNET_CRYPTO_rsa_signature_free (sig); -      GNUNET_CRYPTO_rsa_private_key_free (priv); -      GNUNET_CRYPTO_rsa_public_key_free (pub);        return 1;      }      PQclear (result); -    result = TALER_PQ_exec_prepared (conn, -				     "test_select", -				     params_select); +    result = GNUNET_PQ_exec_prepared (conn, +				      "test_select", +				      params_select);      if (1 !=  	PQntuples (result))      {        GNUNET_break (0);        PQclear (result); -      GNUNET_CRYPTO_rsa_signature_free (sig); -      GNUNET_CRYPTO_rsa_private_key_free (priv); -      GNUNET_CRYPTO_rsa_public_key_free (pub);        return 1;      } -    ret = TALER_PQ_extract_result (result, -				   results_select, -				   0); -    GNUNET_break (GNUNET_YES == ret); -    GNUNET_break (abs_time.abs_value_us == abs_time2.abs_value_us); -    GNUNET_break (forever.abs_value_us == forever2.abs_value_us); -    GNUNET_break (0 == -		  memcmp (&hc, -			  &hc2, -			  sizeof (struct GNUNET_HashCode))); +    ret = GNUNET_PQ_extract_result (result, +				    results_select, +				    0);      GNUNET_break (0 ==  		  TALER_amount_cmp (&hamount,  				    &hamount2)); @@ -237,28 +158,11 @@ run_queries (PGconn *conn)      GNUNET_break (0 ==  		  TALER_amount_cmp (&hamount,  				    &hamount2)); -    GNUNET_break (0 == -		  GNUNET_CRYPTO_rsa_signature_cmp (sig, -						   sig2)); -    GNUNET_break (0 == -		  GNUNET_CRYPTO_rsa_public_key_cmp (pub, -						    pub2)); -    GNUNET_break (strlen (msg) == msg2_len); -    GNUNET_break (0 == -		  strncmp (msg, -			   msg2, -			   msg2_len)); -    GNUNET_break (16 == u162); -    GNUNET_break (32 == u322); -    GNUNET_break (64 == u642);      GNUNET_break (42 == json_integer_value (json_object_get (json2, "foo")));      json_decref (json2); -    TALER_PQ_cleanup_result (results_select); +    GNUNET_PQ_cleanup_result (results_select);      PQclear (result);    } -  GNUNET_CRYPTO_rsa_signature_free (sig); -  GNUNET_CRYPTO_rsa_private_key_free (priv); -  GNUNET_CRYPTO_rsa_public_key_free (pub);    if (GNUNET_OK != ret)      return 1; @@ -290,21 +194,12 @@ main(int argc,    result = PQexec (conn,  		   "CREATE TEMPORARY TABLE IF NOT EXISTS test_pq (" -		   " pub BYTEA NOT NULL" -		   ",sig BYTEA NOT NULL" -		   ",abs_time INT8 NOT NULL" -		   ",forever INT8 NOT NULL" -		   ",hash BYTEA NOT NULL CHECK(LENGTH(hash)=64)" -		   ",hamount_val INT8 NOT NULL" +		   " hamount_val INT8 NOT NULL"  		   ",hamount_frac INT4 NOT NULL"  		   ",hamount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"  		   ",namount_val INT8 NOT NULL"  		   ",namount_frac INT4 NOT NULL"  		   ",namount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" -		   ",vsize VARCHAR NOT NULL" -		   ",u16 INT2 NOT NULL" -		   ",u32 INT4 NOT NULL" -		   ",u64 INT8 NOT NULL"  		   ",json VARCHAR NOT NULL"  		   ")");    if (PGRES_COMMAND_OK != PQresultStatus (result)) | 
