diff options
-rw-r--r-- | src/include/taler_pq_lib.h | 36 | ||||
-rw-r--r-- | src/pq/Makefile.am | 2 | ||||
-rw-r--r-- | src/pq/pq_common.c | 65 | ||||
-rw-r--r-- | src/pq/pq_common.h | 73 | ||||
-rw-r--r-- | src/pq/pq_query_helper.c | 121 | ||||
-rw-r--r-- | src/pq/pq_result_helper.c | 163 |
6 files changed, 425 insertions, 35 deletions
diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h index 25b5f8688..21e48792c 100644 --- a/src/include/taler_pq_lib.h +++ b/src/include/taler_pq_lib.h @@ -45,6 +45,22 @@ TALER_PQ_query_param_amount ( const struct GNUNET_PQ_Context *db, const struct TALER_Amount *amount); + +/** + * Generate query parameter (as record tuple) for an amount, consisting of the + * three components "value", "fraction" and "currency" in this order. The + * types must be a 64-bit integer, a 32-bit integer and a TEXT field of 12 + * characters respectively. + * + * @param db The database context for OID lookup + * @param amount pointer to the query parameter to pass + */ +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_amount_with_currency ( + const struct GNUNET_PQ_Context *db, + const struct TALER_Amount *amount); + + /** * Generate query parameter for a denomination public * key. Internally, the various attributes of the @@ -161,7 +177,24 @@ TALER_PQ_query_param_array_amount ( /** - * Currency amount expected, from a record-field of (DB) taler_amount type + * Currency amount expected, from a record-field of (DB) + * taler_amount_with_currency type. The currenty must be stored in the + * database when using this function. + * + * @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_with_currency ( + const char *name, + struct TALER_Amount *amount); + + +/** + * Currency amount expected, from a record-field of (DB) taler_amount type. + * The currency is NOT stored in the database when using this function, but + * instead passed as the @a currency argument. * * @param name name of the field in the table * @param currency currency to use for @a amount @@ -173,6 +206,7 @@ TALER_PQ_result_spec_amount (const char *name, const char *currency, struct TALER_Amount *amount); + /** * Denomination public key expected. * diff --git a/src/pq/Makefile.am b/src/pq/Makefile.am index c1c9e9bb0..4b192d762 100644 --- a/src/pq/Makefile.am +++ b/src/pq/Makefile.am @@ -10,7 +10,7 @@ lib_LTLIBRARIES = \ libtalerpq.la libtalerpq_la_SOURCES = \ - pq_common.h \ + pq_common.h pq_common.c \ pq_query_helper.c \ pq_result_helper.c libtalerpq_la_LIBADD = \ diff --git a/src/pq/pq_common.c b/src/pq/pq_common.c new file mode 100644 index 000000000..a548a45ca --- /dev/null +++ b/src/pq/pq_common.c @@ -0,0 +1,65 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + 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, see <http://www.gnu.org/licenses/> +*/ +/** + * @file pq/pq_common.c + * @brief common defines for the pq functions + * @author Özgür Kesim + */ +#include "platform.h" +#include "pq_common.h" + +struct TALER_PQ_AmountP +TALER_PQ_make_taler_pq_amount_ ( + const struct TALER_Amount *amount, + uint32_t oid_v, + uint32_t oid_f) +{ + struct TALER_PQ_AmountP rval = { + .oid_v = htonl (oid_v), + .oid_f = htonl (oid_f), + .sz_v = htonl (sizeof((amount)->value)), + .sz_f = htonl (sizeof((amount)->fraction)), + .v = GNUNET_htonll ((amount)->value), + .f = htonl ((amount)->fraction) + }; + + return rval; +} + + +struct TALER_PQ_AmountCurrencyP +TALER_PQ_make_taler_pq_amount_currency_ ( + const struct TALER_Amount *amount, + uint32_t oid_v, + uint32_t oid_f, + uint32_t oid_c) +{ + struct TALER_PQ_AmountCurrencyP rval = { + .oid_v = htonl (oid_v), + .oid_f = htonl (oid_f), + .oid_c = htonl (oid_c), + .sz_v = htonl (sizeof((amount)->value)), + .sz_f = htonl (sizeof((amount)->fraction)), + .sz_c = htonl (TALER_CURRENCY_LEN), + .v = GNUNET_htonll ((amount)->value), + .f = htonl ((amount)->fraction), + }; + + memcpy (rval.c, + amount->currency, + TALER_CURRENCY_LEN); + return rval; +} diff --git a/src/pq/pq_common.h b/src/pq/pq_common.h index d479ce5bb..4dc2d3357 100644 --- a/src/pq/pq_common.h +++ b/src/pq/pq_common.h @@ -21,7 +21,8 @@ #ifndef TALER_PQ_COMMON_H_ #define TALER_PQ_COMMON_H_ -#include "platform.h" +#include "taler_util.h" + /** * Internal types that are supported as TALER-exchange-specific array types. * @@ -43,6 +44,9 @@ enum TALER_PQ_ArrayType TALER_PQ_array_of_blinded_denom_sig, TALER_PQ_array_of_blinded_coin_hash, TALER_PQ_array_of_denom_hash, + /** + * Amounts *without* currency. + */ TALER_PQ_array_of_amount, TALER_PQ_array_of_MAX, /* must be last */ }; @@ -52,7 +56,7 @@ enum TALER_PQ_ArrayType * * All values need to be in network-byte-order. */ -struct TALER_PQ_Amount_P +struct TALER_PQ_AmountP { uint32_t oid_v; /* oid of .v */ uint32_t sz_v; /* size of .v */ @@ -62,23 +66,66 @@ struct TALER_PQ_Amount_P uint32_t f; /* fraction */ } __attribute__((packed)); + +/** + * Memory representation of an taler amount record with currency for Postgres. + * + * All values need to be in network-byte-order. + */ +struct TALER_PQ_AmountCurrencyP +{ + uint32_t oid_v; /* oid of .v */ + uint32_t sz_v; /* size of .v */ + uint64_t v; /* value */ + uint32_t oid_f; /* oid of .f */ + uint32_t sz_f; /* size of .f */ + uint32_t f; /* fraction */ + + /** + * oid of .c + */ + uint32_t oid_c; + + /** + * size of .c + */ + uint32_t sz_c; + + /** + * currency + */ + uint8_t c[TALER_CURRENCY_LEN]; +} __attribute__((packed)); + + +/** + * Create a `struct TALER_PQ_AmountP` for initialization + * + * @param amount amount of type `struct TALER_Amount *` + * @param oid_v OID of the INT8 type in postgres + * @param oid_f OID of the INT4 type in postgres + */ +struct TALER_PQ_AmountP +TALER_PQ_make_taler_pq_amount_ ( + const struct TALER_Amount *amount, + uint32_t oid_v, + uint32_t oid_f); + + /** - * Create a `struct TALER_PQ_Amount_P` for initialization + * Create a `struct TALER_PQ_AmountCurrencyP` for initialization * - * @param db postgres-context of type `struct GNUNET_PQ_Context *` * @param amount amount of type `struct TALER_Amount *` * @param oid_v OID of the INT8 type in postgres * @param oid_f OID of the INT4 type in postgres + * @param oid_c OID of the TEXT type in postgres */ -#define MAKE_TALER_PQ_AMOUNT_P(db,amount,oid_v,oid_f) \ - { \ - .oid_v = htonl (oid_v), \ - .oid_f = htonl (oid_f), \ - .sz_v = htonl (sizeof((amount)->value)), \ - .sz_f = htonl (sizeof((amount)->fraction)), \ - .v = GNUNET_htonll ((amount)->value), \ - .f = htonl ((amount)->fraction) \ - } +struct TALER_PQ_AmountCurrencyP +TALER_PQ_make_taler_pq_amount_currency_ ( + const struct TALER_Amount *amount, + uint32_t oid_v, + uint32_t oid_f, + uint32_t oid_c); #endif /* TALER_PQ_COMMON_H_ */ diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c index 78f29d643..a1a1070bb 100644 --- a/src/pq/pq_query_helper.c +++ b/src/pq/pq_query_helper.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016, 2021, 2022 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA 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 @@ -43,6 +43,102 @@ * @return -1 on error, number of offsets used in @a scratch otherwise */ static int +qconv_amount_currency_tuple (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) +{ + struct GNUNET_PQ_Context *db = cls; + const struct TALER_Amount *amount = data; + size_t sz; + + GNUNET_assert (NULL != db); + GNUNET_assert (NULL != amount); + GNUNET_assert (1 == param_length); + GNUNET_assert (1 <= scratch_length); + GNUNET_assert (sizeof (struct TALER_Amount) == data_len); + GNUNET_static_assert (sizeof(uint32_t) == sizeof(Oid)); + { + char *out; + Oid oid_v; + Oid oid_f; + Oid oid_c; + + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "int8", + &oid_v)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "int4", + &oid_f)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "text", + &oid_c)); + + { + struct TALER_PQ_AmountCurrencyP d + = TALER_PQ_make_taler_pq_amount_currency_ (amount, + oid_v, + oid_f, + oid_c); + + sz = sizeof(uint32_t); /* number of elements in tuple */ + sz += sizeof(d); + out = GNUNET_malloc (sz); + scratch[0] = out; + *(uint32_t *) out = htonl (3); + out += sizeof(uint32_t); + *(struct TALER_PQ_AmountCurrencyP*) out = d; + } + } + + param_values[0] = scratch[0]; + param_lengths[0] = sz; + param_formats[0] = 1; + + return 1; +} + + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_amount_with_currency ( + const struct GNUNET_PQ_Context *db, + const struct TALER_Amount *amount) +{ + struct GNUNET_PQ_QueryParam res = { + .conv_cls = (void *) db, + .conv = &qconv_amount_currency_tuple, + .data = amount, + .size = sizeof (*amount), + .num_params = 1, + }; + + return res; +} + + +/** + * Function called to convert input amount into SQL parameter as tuple. + * + * @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_tuple (void *cls, const void *data, size_t data_len, @@ -78,18 +174,18 @@ qconv_amount_tuple (void *cls, &oid_f)); { - struct TALER_PQ_Amount_P d - = MAKE_TALER_PQ_AMOUNT_P (db, - amount, - oid_v, - oid_f); + struct TALER_PQ_AmountP d + = TALER_PQ_make_taler_pq_amount_ (amount, + oid_v, + oid_f); + sz = sizeof(uint32_t); /* number of elements in tuple */ sz += sizeof(d); out = GNUNET_malloc (sz); scratch[0] = out; *(uint32_t *) out = htonl (2); out += sizeof(uint32_t); - *(struct TALER_PQ_Amount_P*) out = d; + *(struct TALER_PQ_AmountP*) out = d; } } @@ -872,10 +968,11 @@ qconv_array ( "int4", &oid_f)); { - struct TALER_PQ_Amount_P am = MAKE_TALER_PQ_AMOUNT_P (meta->db, - &amounts[i], - oid_v, - oid_f); + struct TALER_PQ_AmountP am + = TALER_PQ_make_taler_pq_amount_ ( + &amounts[i], + oid_v, + oid_f); *(uint32_t *) out = htonl (2); /* number of elements in tuple */ out += sizeof(uint32_t); @@ -1086,7 +1183,7 @@ TALER_PQ_query_param_array_amount ( true, amounts, NULL, - sizeof(uint32_t) + sizeof(struct TALER_PQ_Amount_P), + sizeof(uint32_t) + sizeof(struct TALER_PQ_AmountP), TALER_PQ_array_of_amount, oid, db); diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c index d4810c9ad..3befbdff1 100644 --- a/src/pq/pq_result_helper.c +++ b/src/pq/pq_result_helper.c @@ -25,6 +25,151 @@ /** + * Extract an amount from a tuple including the currency from a Postgres + * database @a result at row @a row. + * + * @param cls closure; not used + * @param result where to extract data from + * @param row 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 enum GNUNET_GenericReturnValue +extract_amount_currency_tuple (void *cls, + PGresult *result, + int row, + const char *fname, + size_t *dst_size, + void *dst) +{ + struct TALER_Amount *r_amount = dst; + int col; + + (void) cls; + if (sizeof (struct TALER_Amount) != *dst_size) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + /* Set return value to invalid in case we don't finish */ + memset (r_amount, + 0, + sizeof (struct TALER_Amount)); + col = PQfnumber (result, + fname); + if (col < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Field `%s' does not exist in result\n", + fname); + return GNUNET_SYSERR; + } + if (PQgetisnull (result, + row, + col)) + { + return GNUNET_NO; + } + + /* Parse the tuple */ + { + char *in; + uint32_t num; + struct TALER_PQ_AmountCurrencyP ap; + int size; + const static int expected_size + = sizeof(uint32_t) /* length */ + + sizeof(ap); + + size = PQgetlength (result, + row, + col); + if (expected_size != size) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Incorrect size of binary field `%s' (got %d, expected %d)\n", + fname, + size, + expected_size); + return GNUNET_SYSERR; + } + + in = PQgetvalue (result, + row, + col); + + num = ntohl (*(uint32_t *) in); + if (3 != num) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Incorrect number of elements in tuple-field `%s'\n", + fname); + return GNUNET_SYSERR; + } + in += sizeof(uint32_t); + memcpy (&ap, + in, + sizeof (ap)); + /* TODO[oec]: OID-checks? */ + + r_amount->value = GNUNET_ntohll (ap.v); + r_amount->fraction = ntohl (ap.f); + memcpy (r_amount->currency, + ap.c, + TALER_CURRENCY_LEN); + if ('\0' != r_amount->currency[TALER_CURRENCY_LEN - 1]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid currency (not 0-terminated) in tuple field `%s'\n", + fname); + /* be sure nobody uses this by accident */ + memset (r_amount, + 0, + sizeof (struct TALER_Amount)); + return GNUNET_SYSERR; + } + } + + if (r_amount->value >= TALER_AMOUNT_MAX_VALUE) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Value in field `%s' exceeds legal range\n", + fname); + return GNUNET_SYSERR; + } + if (r_amount->fraction >= TALER_AMOUNT_FRAC_BASE) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fraction in field `%s' exceeds legal range\n", + fname); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_amount_with_currency (const char *name, + struct TALER_Amount *amount) +{ + struct GNUNET_PQ_ResultSpec res = { + .conv = &extract_amount_currency_tuple, + .dst = (void *) amount, + .dst_size = sizeof (*amount), + .fname = name + }; + + return res; +} + + +/** * Extract an amount from a tuple from a Postgres database @a result at row @a row. * * @param cls closure, a `const char *` giving the currency @@ -49,7 +194,7 @@ extract_amount_tuple (void *cls, struct TALER_Amount *r_amount = dst; const char *currency = cls; int col; - int len; + size_t len; if (sizeof (struct TALER_Amount) != *dst_size) { @@ -81,10 +226,10 @@ extract_amount_tuple (void *cls, { char *in; uint32_t num; - struct TALER_PQ_Amount_P ap; + struct TALER_PQ_AmountP ap; int size; const static int expected_size = sizeof(uint32_t) /* length */ - + sizeof(struct TALER_PQ_Amount_P); + + sizeof(ap); size = PQgetlength (result, row, @@ -113,7 +258,9 @@ extract_amount_tuple (void *cls, return GNUNET_SYSERR; } in += sizeof(uint32_t); - ap = *(struct TALER_PQ_Amount_P *) in; + memcpy (&ap, + in, + sizeof (ap)); /* TODO[oec]: OID-checks? */ @@ -993,14 +1140,14 @@ extract_array_generic ( for (uint32_t i = 0; i < header.dim; i++) { - struct TALER_PQ_Amount_P ap; + struct TALER_PQ_AmountP ap; struct TALER_Amount *amount = &amounts[i]; size_t sz = ntohl (*(uint32_t *) in); in += sizeof(uint32_t); /* total size for this array-entry */ FAIL_IF ((sizeof(uint32_t) - + sizeof(struct TALER_PQ_Amount_P)) + + sizeof(struct TALER_PQ_AmountP)) > sz); /* number of elements in composite type*/ @@ -1008,14 +1155,14 @@ extract_array_generic ( in += sizeof(uint32_t); FAIL_IF (2 != sz); - ap = *(struct TALER_PQ_Amount_P *) in; + ap = *(struct TALER_PQ_AmountP *) in; amount->value = GNUNET_ntohll (ap.v); amount->fraction = ntohl (ap.f); GNUNET_memcpy (amount->currency, info->currency, TALER_CURRENCY_LEN); - in += sizeof(struct TALER_PQ_Amount_P); + in += sizeof(struct TALER_PQ_AmountP); } return GNUNET_OK; } |