diff options
author | Christian Grothoff <christian@grothoff.org> | 2020-07-05 20:43:28 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2020-07-05 20:43:28 +0200 |
commit | 6de49ea2c02e311e5f5366005bd3497a9bb25187 (patch) | |
tree | a221b9c784b4e004eb5972e18516653d1aea6a5c /src/sq | |
parent | e428783e2e0295186dc4eae273df8a3f8b75df60 (diff) | |
parent | ed5b98a2c2308fbd44b906a30286d2689fd304dd (diff) | |
download | exchange-6de49ea2c02e311e5f5366005bd3497a9bb25187.tar.gz exchange-6de49ea2c02e311e5f5366005bd3497a9bb25187.tar.bz2 exchange-6de49ea2c02e311e5f5366005bd3497a9bb25187.zip |
Merge branch 'protocolv8'
Diffstat (limited to 'src/sq')
-rw-r--r-- | src/sq/Makefile.am | 40 | ||||
-rw-r--r-- | src/sq/sq_query_helper.c | 288 | ||||
-rw-r--r-- | src/sq/sq_result_helper.c | 381 | ||||
-rw-r--r-- | src/sq/test_sq.c | 227 |
4 files changed, 936 insertions, 0 deletions
diff --git a/src/sq/Makefile.am b/src/sq/Makefile.am new file mode 100644 index 000000000..ee4c5eba7 --- /dev/null +++ b/src/sq/Makefile.am @@ -0,0 +1,40 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LIBGCRYPT_CFLAGS) $(SQLITE_CPPFLAGS) + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +lib_LTLIBRARIES = \ + libtalersq.la + +libtalersq_la_SOURCES = \ + sq_query_helper.c \ + sq_result_helper.c + +libtalersq_la_LIBADD = \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetutil -ljansson \ + -lsqlite3 $(XLIB) + +libtalersq_la_LDFLAGS = \ + $(SQLITE_LDFLAGS) \ + -version-info 0:0:0 \ + -export-dynamic -no-undefined + +TESTS = \ + test_sq + +check_PROGRAMS= \ + test_sq + +test_sq_SOURCES = \ + test_sq.c +test_sq_LDADD = \ + libtalersq.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetsq \ + -lgnunetutil \ + -ljansson \ + -lsqlite3 $(XLIB) diff --git a/src/sq/sq_query_helper.c b/src/sq/sq_query_helper.c new file mode 100644 index 000000000..e9070e147 --- /dev/null +++ b/src/sq/sq_query_helper.c @@ -0,0 +1,288 @@ +/* + This file is part of TALER + Copyright (C) 2020 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 sq/sq_query_helper.c + * @brief helper functions for Taler-specific SQLite3 interactions + * @author Jonathan Buchanan + */ +#include "platform.h" +#include <sqlite3.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_sq_lib.h> +#include "taler_sq_lib.h" + + +/** + * 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 stmt sqlite statement to parameters for + * @param off offset of the argument to bind in @a stmt, numbered from 1, + * so immediately suitable for passing to `sqlite3_bind`-functions. + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +static int +qconv_amount (void *cls, + const void *data, + size_t data_len, + sqlite3_stmt *stmt, + unsigned int off) +{ + const struct TALER_Amount *amount = data; + + (void) cls; + GNUNET_assert (sizeof (struct TALER_Amount) == data_len); + if (SQLITE_OK != sqlite3_bind_int64 (stmt, + (int) off, + (sqlite3_int64) amount->value)) + return GNUNET_SYSERR; + if (SQLITE_OK != sqlite3_bind_int64 (stmt, + (int) off + 1, + (sqlite3_int64) amount->fraction)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Generate query parameter for a currency, consisting of the + * components "value", "fraction" in this order. The + * types must be a 64-bit integer and a 64-bit integer. + * + * @param x pointer to the query parameter to pass + */ +struct GNUNET_SQ_QueryParam +TALER_SQ_query_param_amount (const struct TALER_Amount *x) +{ + struct GNUNET_SQ_QueryParam res = + { &qconv_amount, NULL, x, sizeof (*x), 2 }; + return res; +} + + +/** + * 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 stmt sqlite statement to parameters for + * @param off offset of the argument to bind in @a stmt, numbered from 1, + * so immediately suitable for passing to `sqlite3_bind`-functions. + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +static int +qconv_amount_nbo (void *cls, + const void *data, + size_t data_len, + sqlite3_stmt *stmt, + unsigned int off) +{ + const struct TALER_AmountNBO *amount = data; + struct TALER_Amount amount_hbo; + + (void) cls; + TALER_amount_ntoh (&amount_hbo, + amount); + return qconv_amount (cls, + &amount_hbo, + sizeof (struct TALER_Amount), + stmt, + off); +} + + +/** + * Generate query parameter for a currency, consisting of the + * components "value", "fraction" in this order. The + * types must be a 64-bit integer and a 64-bit integer. + * + * @param x pointer to the query parameter to pass + */ +struct GNUNET_SQ_QueryParam +TALER_SQ_query_param_amount_nbo (const struct TALER_AmountNBO *x) +{ + struct GNUNET_SQ_QueryParam res = + { &qconv_amount_nbo, NULL, x, sizeof (*x), 2 }; + 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 stmt sqlite statement to parameters for + * @param off offset of the argument to bind in @a stmt, numbered from 1, + * so immediately suitable for passing to `sqlite3_bind`-functions. + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +static int +qconv_json (void *cls, + const void *data, + size_t data_len, + sqlite3_stmt *stmt, + unsigned int off) +{ + const json_t *json = data; + char *str; + + (void) cls; + (void) data_len; + str = json_dumps (json, JSON_COMPACT); + if (NULL == str) + return GNUNET_SYSERR; + + if (SQLITE_OK != sqlite3_bind_text (stmt, + (int) off, + str, + strlen (str) + 1, + SQLITE_TRANSIENT)) + return GNUNET_SYSERR; + GNUNET_free (str); + return GNUNET_OK; +} + + +/** + * Generate query parameter for a JSON object (stored as a string + * in the DB). Note that @a x must really be a JSON object or array, + * passing just a value (string, integer) is not supported and will + * result in an abort. + * + * @param x pointer to the json object to pass + */ +struct GNUNET_SQ_QueryParam +TALER_SQ_query_param_json (const json_t *x) +{ + struct GNUNET_SQ_QueryParam res = + { &qconv_json, NULL, x, sizeof (*x), 1 }; + 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 stmt sqlite statement to parameters for + * @param off offset of the argument to bind in @a stmt, numbered from 1, + * so immediately suitable for passing to `sqlite3_bind`-functions. + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +static int +qconv_round_time (void *cls, + const void *data, + size_t data_len, + sqlite3_stmt *stmt, + unsigned int off) +{ + const struct GNUNET_TIME_Absolute *at = data; + struct GNUNET_TIME_Absolute tmp; + + (void) cls; + GNUNET_assert (sizeof (struct GNUNET_TIME_AbsoluteNBO) == data_len); + GNUNET_break (NULL == cls); + tmp = *at; + GNUNET_assert (GNUNET_OK == + GNUNET_TIME_round_abs (&tmp)); + if (SQLITE_OK != sqlite3_bind_int64 (stmt, + (int) off, + (sqlite3_int64) at->abs_value_us)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Generate query parameter for an absolute time value. + * In contrast to + * #GNUNET_SQ_query_param_absolute_time(), this function + * will abort (!) if the time given is not rounded! + * The database must store a 64-bit integer. + * + * @param x pointer to the query parameter to pass + */ +struct GNUNET_SQ_QueryParam +TALER_SQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x) +{ + struct GNUNET_SQ_QueryParam res = + { &qconv_round_time, NULL, x, sizeof (*x), 1 }; + 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 stmt sqlite statement to parameters for + * @param off offset of the argument to bind in @a stmt, numbered from 1, + * so immediately suitable for passing to `sqlite3_bind`-functions. + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +static int +qconv_round_time_abs (void *cls, + const void *data, + size_t data_len, + sqlite3_stmt *stmt, + unsigned int off) +{ + const struct GNUNET_TIME_AbsoluteNBO *at = data; + struct GNUNET_TIME_Absolute tmp; + + (void) cls; + GNUNET_assert (sizeof (struct GNUNET_TIME_AbsoluteNBO) == data_len); + GNUNET_break (NULL == cls); + tmp = GNUNET_TIME_absolute_ntoh (*at); + GNUNET_assert (GNUNET_OK == + GNUNET_TIME_round_abs (&tmp)); + if (SQLITE_OK != sqlite3_bind_int64 (stmt, + (int) off, + (sqlite3_int64) tmp.abs_value_us)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Generate query parameter for an absolute time value. + * In contrast to + * #GNUNET_SQ_query_param_absolute_time(), this function + * will abort (!) if the time given is not rounded! + * The database must store a 64-bit integer. + * + * @param x pointer to the query parameter to pass + */ +struct GNUNET_SQ_QueryParam +TALER_SQ_query_param_absolute_time_nbo (const struct + GNUNET_TIME_AbsoluteNBO *x) +{ + struct GNUNET_SQ_QueryParam res = + { &qconv_round_time_abs, NULL, x, sizeof (*x), 1 }; + return res; +} + + +/* end of sq/sq_query_helper.c */ diff --git a/src/sq/sq_result_helper.c b/src/sq/sq_result_helper.c new file mode 100644 index 000000000..bc52913b5 --- /dev/null +++ b/src/sq/sq_result_helper.c @@ -0,0 +1,381 @@ +/* + This file is part of TALER + Copyright (C) 2020 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 sq/sq_result_helper.c + * @brief functions to initialize parameter arrays + * @author Jonathan Buchanan + */ +#include "platform.h" +#include <sqlite3.h> +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_sq_lib.h> +#include "taler_sq_lib.h" +#include "taler_amount_lib.h" + + +/** + * Extract amount data from a SQLite database + * + * @param cls closure, a `const char *` giving the currency + * @param result where to extract data from + * @param column column to extract data 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_SYSERR if a result was invalid (non-existing field or NULL) + */ +static int +extract_amount (void *cls, + sqlite3_stmt *result, + unsigned int column, + size_t *dst_size, + void *dst) +{ + struct TALER_Amount *amount = dst; + const char *currency = cls; + if ((sizeof (struct TALER_Amount) != *dst_size) || + (SQLITE_INTEGER != sqlite3_column_type (result, + (int) column)) || + (SQLITE_INTEGER != sqlite3_column_type (result, + (int) column + 1))) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_strlcpy (amount->currency, + currency, + TALER_CURRENCY_LEN); + amount->value = (uint64_t) sqlite3_column_int64 (result, + (int) column); + uint64_t frac = (uint64_t) sqlite3_column_int64 (result, + (int) column + 1); + amount->fraction = (uint32_t) frac; + return GNUNET_YES; +} + + +/** + * Currency amount expected. + * + * @param currency the currency to use for @a amount + * @param[out] amount where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_SQ_ResultSpec +TALER_SQ_result_spec_amount (const char *currency, + struct TALER_Amount *amount) +{ + struct GNUNET_SQ_ResultSpec res = { + .conv = &extract_amount, + .cls = (void *) currency, + .dst = (void *) amount, + .dst_size = sizeof (struct TALER_Amount), + .num_params = 2 + }; + + return res; +} + + +/** + * Extract amount data from a SQLite database + * + * @param cls closure, a `const char *` giving the currency + * @param result where to extract data from + * @param column column to extract data 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_SYSERR if a result was invalid (non-existing field or NULL) + */ +static int +extract_amount_nbo (void *cls, + sqlite3_stmt *result, + unsigned int column, + size_t *dst_size, + void *dst) +{ + struct TALER_AmountNBO *amount = dst; + struct TALER_Amount amount_hbo; + size_t amount_hbo_size = sizeof (struct TALER_Amount); + if (GNUNET_YES != extract_amount (cls, + result, + column, + &amount_hbo_size, + &amount_hbo)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + TALER_amount_hton (amount, + &amount_hbo); + return GNUNET_YES; +} + + +/** + * Currency amount expected. + * + * @param currency the currency to use for @a amount + * @param[out] amount where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_SQ_ResultSpec +TALER_SQ_result_spec_amount_nbo (const char *currency, + struct TALER_AmountNBO *amount) +{ + struct GNUNET_SQ_ResultSpec res = { + .conv = &extract_amount_nbo, + .cls = (void *) currency, + .dst = (void *) amount, + .dst_size = sizeof (struct TALER_AmountNBO), + .num_params = 2 + }; + + return res; +} + + +/** + * Extract amount data from a SQLite database + * + * @param cls closure + * @param result where to extract data from + * @param column column to extract data 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_SYSERR if a result was invalid (non-existing field or NULL) + */ +static int +extract_json (void *cls, + sqlite3_stmt *result, + unsigned int column, + size_t *dst_size, + void *dst) +{ + json_t **j_dst = dst; + const char *res; + json_error_t json_error; + size_t slen; + + (void) cls; + (void) dst_size; + if (SQLITE_TEXT != sqlite3_column_type (result, + column)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + res = (const char *) sqlite3_column_text (result, + column); + slen = strlen (res); + *j_dst = json_loadb (res, + slen, + JSON_REJECT_DUPLICATES, + &json_error); + if (NULL == *j_dst) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse JSON result for column %d: %s (%s)\n", + column, + json_error.text, + json_error.source); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function called to clean up memory allocated + * by a #GNUNET_SQ_ResultConverter. + * + * @param cls closure + */ +static void +clean_json (void *cls) +{ + json_t **dst = cls; + + (void) cls; + if (NULL != *dst) + { + json_decref (*dst); + *dst = NULL; + } +} + + +/** + * json_t expected. + * + * @param[out] jp where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_SQ_ResultSpec +TALER_SQ_result_spec_json (json_t **jp) +{ + struct GNUNET_SQ_ResultSpec res = { + .conv = &extract_json, + .cleaner = &clean_json, + .dst = (void *) jp, + .cls = (void *) jp, + .num_params = 1 + }; + + return res; +} + + +/** + * Extract amount data from a SQLite database + * + * @param cls closure + * @param result where to extract data from + * @param column column to extract data 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_SYSERR if a result was invalid (non-existing field or NULL) + */ +static int +extract_round_time (void *cls, + sqlite3_stmt *result, + unsigned int column, + size_t *dst_size, + void *dst) +{ + struct GNUNET_TIME_Absolute *udst = dst; + struct GNUNET_TIME_Absolute tmp; + + (void) cls; + if (SQLITE_INTEGER != sqlite3_column_type (result, + (int) column)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_assert (NULL != dst); + if (sizeof (struct GNUNET_TIME_Absolute) != *dst_size) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + tmp.abs_value_us = sqlite3_column_int64 (result, + (int) column); + GNUNET_break (GNUNET_OK == + GNUNET_TIME_round_abs (&tmp)); + *udst = tmp; + return GNUNET_OK; +} + + +/** + * Rounded absolute time expected. + * In contrast to #GNUNET_SQ_query_param_absolute_time_nbo(), + * this function ensures that the result is rounded and can + * be converted to JSON. + * + * @param[out] at where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_SQ_ResultSpec +TALER_SQ_result_spec_absolute_time (struct GNUNET_TIME_Absolute *at) +{ + struct GNUNET_SQ_ResultSpec res = { + .conv = &extract_round_time, + .dst = (void *) at, + .dst_size = sizeof (struct GNUNET_TIME_Absolute), + .num_params = 1 + }; + + return res; +} + + +/** + * Extract amount data from a SQLite database + * + * @param cls closure + * @param result where to extract data from + * @param column column to extract data 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_SYSERR if a result was invalid (non-existing field or NULL) + */ +static int +extract_round_time_nbo (void *cls, + sqlite3_stmt *result, + unsigned int column, + size_t *dst_size, + void *dst) +{ + struct GNUNET_TIME_AbsoluteNBO *udst = dst; + struct GNUNET_TIME_Absolute tmp; + + (void) cls; + if (SQLITE_INTEGER != sqlite3_column_type (result, + (int) column)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_assert (NULL != dst); + if (sizeof (struct GNUNET_TIME_AbsoluteNBO) != *dst_size) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + tmp.abs_value_us = sqlite3_column_int64 (result, + (int) column); + GNUNET_break (GNUNET_OK == + GNUNET_TIME_round_abs (&tmp)); + *udst = GNUNET_TIME_absolute_hton (tmp); + return GNUNET_OK; +} + + +/** + * Rounded absolute time expected. + * In contrast to #GNUNET_SQ_result_spec_absolute_time_nbo(), + * this function ensures that the result is rounded and can + * be converted to JSON. + * + * @param[out] at where to store the result + * @return array entry for the result specification to use + */ +struct GNUNET_SQ_ResultSpec +TALER_SQ_result_spec_absolute_time_nbo (struct GNUNET_TIME_AbsoluteNBO *at) +{ + struct GNUNET_SQ_ResultSpec res = { + .conv = &extract_round_time_nbo, + .dst = (void *) at, + .dst_size = sizeof (struct GNUNET_TIME_AbsoluteNBO), + .num_params = 1 + }; + + return res; +} + + +/* end of sq/sq_result_helper.c */ diff --git a/src/sq/test_sq.c b/src/sq/test_sq.c new file mode 100644 index 000000000..308b65b06 --- /dev/null +++ b/src/sq/test_sq.c @@ -0,0 +1,227 @@ +/* + This file is part of TALER + Copyright (C) 2020 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 sq/test_sq.c + * @brief Tests for SQLite3 convenience API + * @author Jonathan Buchanan + */ +#include "platform.h" +#include "taler_sq_lib.h" + + +/** + * Run actual test queries. + * + * @return 0 on success + */ +static int +run_queries (sqlite3 *db) +{ + struct TALER_Amount hamount; + struct TALER_AmountNBO namount; + json_t *json; + struct GNUNET_TIME_Absolute htime = GNUNET_TIME_absolute_get (); + struct GNUNET_TIME_AbsoluteNBO ntime; + sqlite3_stmt *test_insert; + sqlite3_stmt *test_select; + struct GNUNET_SQ_PrepareStatement ps[] = { + GNUNET_SQ_make_prepare ("INSERT INTO test_sq (" + " hamount_val" + ",hamount_frac" + ",namount_val" + ",namount_frac" + ",json" + ",htime" + ",ntime" + ") VALUES " + "($1, $2, $3, $4, $5, $6, $7)", + &test_insert), + GNUNET_SQ_make_prepare ("SELECT" + " hamount_val" + ",hamount_frac" + ",namount_val" + ",namount_frac" + ",json" + ",htime" + ",ntime" + " FROM test_sq", + &test_select), + GNUNET_SQ_PREPARE_END + }; + int ret = 0; + + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:1.23", + &hamount)); + TALER_amount_hton (&namount, + &hamount); + json = json_object (); + json_object_set_new (json, "foo", json_integer (42)); + GNUNET_assert (NULL != json); + GNUNET_TIME_round_abs (&htime); + ntime = GNUNET_TIME_absolute_hton (htime); + + GNUNET_assert (GNUNET_OK == GNUNET_SQ_prepare (db, + ps)); + + { + struct GNUNET_SQ_QueryParam params_insert[] = { + TALER_SQ_query_param_amount (&hamount), + TALER_SQ_query_param_amount_nbo (&namount), + TALER_SQ_query_param_json (json), + TALER_SQ_query_param_absolute_time (&htime), + TALER_SQ_query_param_absolute_time_nbo (&ntime), + GNUNET_SQ_query_param_end + }; + GNUNET_SQ_reset (db, + test_insert); + GNUNET_assert (GNUNET_OK == GNUNET_SQ_bind (test_insert, + params_insert)); + GNUNET_assert (SQLITE_DONE == sqlite3_step (test_insert)); + sqlite3_finalize (test_insert); + } + + { + struct TALER_Amount result_amount; + struct TALER_AmountNBO nresult_amount; + struct TALER_Amount nresult_amount_converted; + json_t *result_json; + struct GNUNET_TIME_Absolute hresult_time; + struct GNUNET_TIME_AbsoluteNBO nresult_time; + struct GNUNET_SQ_QueryParam params_select[] = { + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_ResultSpec results_select[] = { + TALER_SQ_result_spec_amount ("EUR", + &result_amount), + TALER_SQ_result_spec_amount_nbo ("EUR", + &nresult_amount), + TALER_SQ_result_spec_json (&result_json), + TALER_SQ_result_spec_absolute_time (&hresult_time), + TALER_SQ_result_spec_absolute_time_nbo (&nresult_time), + GNUNET_SQ_result_spec_end + }; + + GNUNET_SQ_reset (db, + test_select); + GNUNET_assert (GNUNET_OK == GNUNET_SQ_bind (test_select, + params_select)); + GNUNET_assert (SQLITE_ROW == sqlite3_step (test_select)); + + GNUNET_assert (GNUNET_OK == GNUNET_SQ_extract_result (test_select, + results_select)); + TALER_amount_ntoh (&nresult_amount_converted, + &nresult_amount); + if ((GNUNET_OK != TALER_amount_cmp_currency (&hamount, + &result_amount)) || + (0 != TALER_amount_cmp (&hamount, + &result_amount)) || + (GNUNET_OK != TALER_amount_cmp_currency (&hamount, + &nresult_amount_converted)) || + (0 != TALER_amount_cmp (&hamount, + &nresult_amount_converted)) || + (1 != json_equal (json, + result_json)) || + (htime.abs_value_us != hresult_time.abs_value_us) || + (ntime.abs_value_us__ != nresult_time.abs_value_us__)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Result from database doesn't match input\n"); + ret = 1; + } + GNUNET_SQ_cleanup_result (results_select); + sqlite3_finalize (test_select); + } + json_decref (json); + + return ret; +} + + +int +main (int argc, + const char *const argv[]) +{ + struct GNUNET_SQ_ExecuteStatement es[] = { + GNUNET_SQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_sq (" + " hamount_val INT8 NOT NULL" + ",hamount_frac INT8 NOT NULL" + ",namount_val INT8 NOT NULL" + ",namount_frac INT8 NOT NULL" + ",json VARCHAR NOT NULL" + ",htime INT8 NOT NULL" + ",ntime INT8 NOT NULL" + ")"), + GNUNET_SQ_EXECUTE_STATEMENT_END + }; + sqlite3 *db; + int ret; + + GNUNET_log_setup ("test-pq", + "WARNING", + NULL); + + if (SQLITE_OK != sqlite3_open ("talercheck.db", + &db)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to open SQLite3 database\n"); + return 77; + } + + if (GNUNET_OK != GNUNET_SQ_exec_statements (db, + es)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to create new table\n"); + if ((SQLITE_OK != sqlite3_close (db)) || + (0 != unlink ("talercheck.db"))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to close db or unlink\n"); + } + return 1; + } + + ret = run_queries (db); + + if (SQLITE_OK != + sqlite3_exec (db, + "DROP TABLE test_sq", + NULL, NULL, NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to drop table\n"); + ret = 1; + } + + if (SQLITE_OK != sqlite3_close (db)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to close database\n"); + ret = 1; + } + if (0 != unlink ("talercheck.db")) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to unlink test database file\n"); + ret = 1; + } + return ret; +} + + +/* end of sq/test_sq.c */ |