summaryrefslogtreecommitdiff
path: root/src/sq
diff options
context:
space:
mode:
Diffstat (limited to 'src/sq')
-rw-r--r--src/sq/Makefile.am40
-rw-r--r--src/sq/sq_query_helper.c77
-rw-r--r--src/sq/sq_result_helper.c92
-rw-r--r--src/sq/test_sq.c180
4 files changed, 389 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..8116622a5
--- /dev/null
+++ b/src/sq/sq_query_helper.c
@@ -0,0 +1,77 @@
+/*
+ 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_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;
+
+ GNUNET_assert (sizeof (struct TALER_AmountNBO) == 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 three
+ * 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;
+}
+
+
+/* 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..ef36d3e90
--- /dev/null
+++ b/src/sq/sq_result_helper.c
@@ -0,0 +1,92 @@
+/*
+ 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_nbo (void *cls,
+ sqlite3_stmt *result,
+ unsigned int column,
+ size_t *dst_size,
+ void *dst)
+{
+ struct TALER_AmountNBO *amount = dst;
+ const char *currency = cls;
+ if ((sizeof (struct TALER_AmountNBO) != *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,
+ 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_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;
+}
+
+
+/* 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..85f837e40
--- /dev/null
+++ b/src/sq/test_sq.c
@@ -0,0 +1,180 @@
+/*
+ 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;
+ sqlite3_stmt *test_insert;
+ sqlite3_stmt *test_select;
+ struct GNUNET_SQ_PrepareStatement ps[] = {
+ GNUNET_SQ_make_prepare ("INSERT INTO test_sq ("
+ " namount_val"
+ ",namount_frac"
+ ") VALUES "
+ "($1, $2)",
+ &test_insert),
+ GNUNET_SQ_make_prepare ("SELECT"
+ " namount_val"
+ ",namount_frac"
+ " FROM test_sq",
+ &test_select),
+ GNUNET_SQ_PREPARE_END
+ };
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount ("EUR:1.23",
+ &hamount));
+ TALER_amount_hton (&namount,
+ &hamount);
+
+ GNUNET_assert (GNUNET_OK == GNUNET_SQ_prepare (db,
+ ps));
+
+ {
+ struct GNUNET_SQ_QueryParam params_insert[] = {
+ TALER_SQ_query_param_amount_nbo (&namount),
+ 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_AmountNBO nresult_amount;
+ struct TALER_Amount result_amount;
+ struct GNUNET_SQ_QueryParam params_select[] = {
+ GNUNET_SQ_query_param_end
+ };
+ struct GNUNET_SQ_ResultSpec results_select[] = {
+ TALER_SQ_result_spec_amount_nbo ("EUR",
+ &nresult_amount),
+ 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));
+ GNUNET_SQ_cleanup_result (results_select);
+ sqlite3_finalize (test_select);
+ TALER_amount_ntoh (&result_amount, &nresult_amount);
+ if ((GNUNET_OK != TALER_amount_cmp_currency (&hamount,
+ &result_amount)) ||
+ (0 != TALER_amount_cmp (&hamount,
+ &result_amount)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Result from database doesn't match input\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+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 ("
+ " namount_val INT8 NOT NULL"
+ ",namount_frac INT4 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 */