From 85e59ceb1a704db77b8d48b5e9ccc26c0283559b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 6 May 2015 11:13:09 +0200 Subject: expanding PQ APIs to be able to handle Amounts nicely (and be extensible for additional data types in the future) --- src/include/taler_pq_lib.h | 65 ++++++++++--- src/pq/db_pq.c | 235 ++++++++++++++++++++++++++++++++------------- 2 files changed, 220 insertions(+), 80 deletions(-) diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index 6d4ac8cb8..f6f64178a 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -26,11 +26,47 @@ #include #include "taler_util.h" +/** + * Different formats of results that can be added to a query. + */ +enum TALER_PQ_QueryFormat +{ + + /** + * List terminator. + */ + TALER_PQ_QF_END, + + /** + * We have a fixed-size result (binary blob, no endianess conversion). + */ + TALER_PQ_QF_FIXED_BLOB, + + /** + * We have a variable-size result (binary blob, no endianess conversion). + */ + TALER_PQ_QF_VARSIZE_BLOB, + + /** + * We have a currency amount (with endianess conversion). + * Data points to a `struct TALER_AmountNBO`, size is not used. + */ + TALER_PQ_QF_AMOUNT_NBO +}; + + /** * @brief Description of a DB query parameter. */ struct TALER_PQ_QueryParam { + + /** + * Format of the rest of the entry, determines the data + * type that is being added to the query. + */ + enum TALER_PQ_QueryFormat format; + /** * Data or NULL. */ @@ -41,18 +77,13 @@ struct TALER_PQ_QueryParam */ size_t size; - /** - * Non-null if this is not the last parameter. - * This allows us to detect the end of the list. - */ - int more; }; /** * End of query parameter specification. */ -#define TALER_PQ_QUERY_PARAM_END { NULL, 0, 0 } +#define TALER_PQ_QUERY_PARAM_END { TALER_PQ_QF_END, NULL, 0 } /** * Generate fixed-size query parameter with size given explicitly. @@ -60,7 +91,7 @@ struct TALER_PQ_QueryParam * @param x pointer to the query parameter to pass * @param s number of bytes of @a x to use for the query */ -#define TALER_PQ_QUERY_PARAM_PTR_SIZED(x, s) { (x), (s), 1 } +#define TALER_PQ_QUERY_PARAM_PTR_SIZED(x, s) { TALER_PQ_QF_FIXED_BLOB, (x), (s) } /** * Generate fixed-size query parameter with size determined @@ -68,7 +99,15 @@ struct TALER_PQ_QueryParam * * @param x pointer to the query parameter to pass. */ -#define TALER_PQ_QUERY_PARAM_PTR(x) TALER_PQ_QUERY_PARAM_PTR_SIZED(x, sizeof (*(x))) +#define TALER_PQ_QUERY_PARAM_PTR(x) { TALER_PQ_QF_VARSIZE_BLOB, x, sizeof (*(x)) } + +/** + * Generate fixed-size query parameter with size determined + * by variable type. + * + * @param x pointer to the query parameter to pass. + */ +#define TALER_PQ_QUERY_PARAM_AMOUNT_NBO(x) { TALER_PQ_QF_AMOUNT_NBO, x, sizeof (*(x)) } /** @@ -93,10 +132,10 @@ enum TALER_PQ_ResultFormat TALER_PQ_RF_VARSIZE_BLOB, /** - * We have a currency amount (with endianess conversion). - * Data points to a `struct TALER_Amount`, size is not used. + * We have a currency amount. + * Data points to a `struct TALER_AmountNBO`, size is not used. */ - TALER_PQ_RF_AMOUNT + TALER_PQ_RF_AMOUNT_NBO }; @@ -176,7 +215,7 @@ struct TALER_PQ_ResultSpec * @param name name of the field in the table * @param amount a `struct TALER_Amount` where to store the result */ -#define TALER_PQ_RESULT_SPEC_AMOUNT(name, amount) {TALER_PQ_RF_AMOUNT, (void *) (&dst), 0, (name), sptr } +#define TALER_PQ_RESULT_SPEC_AMOUNT(name, amount) {TALER_PQ_RF_AMOUNT, (void *) (&dst), sizeof (amount), (name), NULL } /** @@ -264,4 +303,4 @@ TALER_PQ_extract_amount (PGresult *result, #endif /* TALER_PQ_LIB_H_ */ -/* end of db/taler_pq_lib.h */ +/* end of include/taler_pq_lib.h */ diff --git a/src/pq/db_pq.c b/src/pq/db_pq.c index 1ec6e8a73..2c0fccfa9 100644 --- a/src/pq/db_pq.c +++ b/src/pq/db_pq.c @@ -27,6 +27,11 @@ /** * 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, @@ -37,11 +42,26 @@ TALER_PQ_exec_prepared (PGconn *db_conn, 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; - for (len = 0, x = params; - x->more; - len++, x++); + const struct TALER_PQ_QueryParam *x = ¶ms[i]; + + switch (x->format) + { + case TALER_PQ_RF_FIXED_BLOB: + case TALER_PQ_RF_VARSIZE_BLOB: + len++; + break; + case TALER_PQ_RF_AMOUNT_NBO: + len += 3; + break; + default: + /* format not supported */ + GNUNET_assert (0); + break; + } } /* new scope to allow stack allocation without alloca */ @@ -49,13 +69,48 @@ TALER_PQ_exec_prepared (PGconn *db_conn, void *param_values[len]; int param_lengths[len]; int param_formats[len]; + unsigned int off; - for (i = 0; i < len; i += 1) + i = 0; + off = 0; + while (TALER_PQ_QF_END != params[i].format) { - param_values[i] = (void *) params[i].data; - param_lengths[i] = params[i].size; - param_formats[i] = 1; + const struct TALER_PQ_QueryParam *x = ¶ms[i]; + + switch (x->format) + { + case TALER_PQ_RF_FIXED_BLOB: + case TALER_PQ_RF_VARSIZE_BLOB: + param_values[off] = (void *) x->data; + param_lengths[off] = x->size; + param_formats[off] = 1; + off++; + break; + case TALER_PQ_RF_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) + 1; + param_formats[off] = 1; + off++; + } + break; + default: + /* format not supported */ + GNUNET_assert (0); + break; + } } + GNUNET_assert (off == len); return PQexecPrepared (db_conn, name, len, @@ -85,75 +140,121 @@ TALER_PQ_extract_result (PGresult *result, struct TALER_PQ_ResultSpec *rs, int row) { - int had_null = GNUNET_NO; - size_t len; unsigned int i; - unsigned int j; - const char *res; - void *dst; - int fnum; + int had_null = GNUNET_NO; - for (i=0; NULL != rs[i].fname; i++) + for (i=0; TALER_PQ_RF_END != rs[i].format; i++) { - fnum = PQfnumber (result, - rs[i].fname); - if (fnum < 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Field `%s' does not exist in result\n", - rs[i].fname); - return GNUNET_SYSERR; - } + struct TALER_PQ_ResultSpec *spec; - /* if a field is null, continue but - * remember that we now return a different result */ - if (PQgetisnull (result, - row, - fnum)) - { - had_null = GNUNET_YES; - continue; - } - len = PQgetlength (result, - row, - fnum); - if ( (0 != rs[i].dst_size) && - (rs[i].dst_size != len) ) + spec = &rs[i]; + switch (spec->format) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Field `%s' has wrong size (got %u, expected %u)\n", - rs[i].fname, - (unsigned int) len, - (unsigned int) rs[i].dst_size); - for (j=0; jfname); + 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 != rs[i].dst_size) && + (rs[i].dst_size != len) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Field `%s' has wrong size (got %u, expected %u)\n", + rs[i].fname, + (unsigned int) len, + (unsigned int) rs[i].dst_size); + for (j=0; jfname; + + GNUNET_assert (NULL != rs[i].dst); + GNUNET_asprintf (&val_name, + "%s_val", + name); + GNUNET_asprintf (&frac_name, + "%s_frac", + name); + GNUNET_asprintf (&curr_name, + "%s_curr", + name); + + if (GNUNET_YES != + TALER_PQ_extract_amount_nbo (result, + row, + val_name, + frac_name, + curr_name, + rs[i].dst)) + had_null = GNUNET_YES; + GNUNET_free (val_name); + GNUNET_free (frac_name); + GNUNET_free (curr_name); + break; + } + default: + GNUNET_assert (0); + break; } - else - dst = rs[i].dst; - memcpy (dst, - res, - len); } if (GNUNET_YES == had_null) return GNUNET_NO; -- cgit v1.2.3