aboutsummaryrefslogtreecommitdiff
path: root/src/sq
diff options
context:
space:
mode:
authorJonathan Buchanan <jonathan.russ.buchanan@gmail.com>2020-06-02 14:20:55 -0400
committerChristian Grothoff <christian@grothoff.org>2020-07-05 16:50:13 +0200
commit83319e1782da625a0a323bd5cfbf9b155e929a06 (patch)
treebcd4d4fc00da2595a8d3171412e0f375571e3c61 /src/sq
parentef0e79927cbd64832b7351074cf71853212a331b (diff)
implemented the other functions for taler_sq_lib
Diffstat (limited to 'src/sq')
-rw-r--r--src/sq/sq_query_helper.c216
-rw-r--r--src/sq/sq_result_helper.c293
-rw-r--r--src/sq/test_sq.c71
3 files changed, 567 insertions, 13 deletions
diff --git a/src/sq/sq_query_helper.c b/src/sq/sq_query_helper.c
index 8116622a5..8e3853553 100644
--- a/src/sq/sq_query_helper.c
+++ b/src/sq/sq_query_helper.c
@@ -45,6 +45,7 @@ qconv_amount_nbo (void *cls,
{
const struct TALER_AmountNBO *amount = data;
+ (void) cls;
GNUNET_assert (sizeof (struct TALER_AmountNBO) == data_len);
if (SQLITE_OK != sqlite3_bind_int64 (stmt,
(int) off,
@@ -59,7 +60,7 @@ qconv_amount_nbo (void *cls,
/**
- * Generate query parameter for a currency, consisting of the three
+ * 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.
*
@@ -74,4 +75,217 @@ TALER_SQ_query_param_amount_nbo (const struct TALER_AmountNBO *x)
}
+/**
+ * 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_hbo = data;
+ struct TALER_AmountNBO amount;
+
+ (void) cls;
+ GNUNET_assert (sizeof (struct TALER_AmountNBO) == data_len);
+ TALER_amount_hton (&amount,
+ amount_hbo);
+ return qconv_amount_nbo (cls,
+ &amount,
+ sizeof (struct TALER_AmountNBO),
+ 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 (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_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;
+ struct GNUNET_TIME_AbsoluteNBO buf;
+
+ (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));
+ buf = GNUNET_TIME_absolute_hton (tmp);
+ if (SQLITE_OK != sqlite3_bind_int64 (stmt,
+ (int) off,
+ (sqlite3_int64) buf.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) 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_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
index ef36d3e90..d313ed599 100644
--- a/src/sq/sq_result_helper.c
+++ b/src/sq/sq_result_helper.c
@@ -89,4 +89,297 @@ TALER_SQ_result_spec_amount_nbo (const char *currency,
}
+/**
+ * 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;
+ struct TALER_AmountNBO amount_nbo;
+ if (GNUNET_YES == extract_amount_nbo (cls,
+ result,
+ column,
+ dst_size,
+ &amount_nbo))
+ {
+ TALER_amount_ntoh (amount,
+ &amount_nbo);
+ return GNUNET_YES;
+ }
+ else
+ {
+ return GNUNET_SYSERR;
+ }
+
+}
+
+
+/**
+ * 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
+ * @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_AbsoluteNBO res;
+ 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;
+ }
+ res.abs_value_us__ = sqlite3_column_int64 (result,
+ (int) column);
+ tmp = GNUNET_TIME_absolute_ntoh (res);
+ 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_AbsoluteNBO res;
+ 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;
+ }
+ res.abs_value_us__ = sqlite3_column_int64 (result,
+ (int) column);
+ tmp = GNUNET_TIME_absolute_ntoh (res);
+ 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
index 85f837e40..308b65b06 100644
--- a/src/sq/test_sq.c
+++ b/src/sq/test_sq.c
@@ -32,35 +32,58 @@ 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 ("
- " namount_val"
+ " hamount_val"
+ ",hamount_frac"
+ ",namount_val"
",namount_frac"
+ ",json"
+ ",htime"
+ ",ntime"
") VALUES "
- "($1, $2)",
+ "($1, $2, $3, $4, $5, $6, $7)",
&test_insert),
GNUNET_SQ_make_prepare ("SELECT"
- " namount_val"
+ " 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,
@@ -72,14 +95,23 @@ run_queries (sqlite3 *db)
}
{
- struct TALER_AmountNBO nresult_amount;
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
};
@@ -91,21 +123,31 @@ run_queries (sqlite3 *db)
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);
+ 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)))
+ &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");
- return 1;
+ ret = 1;
}
+ GNUNET_SQ_cleanup_result (results_select);
+ sqlite3_finalize (test_select);
}
+ json_decref (json);
- return 0;
+ return ret;
}
@@ -115,8 +157,13 @@ main (int argc,
{
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"
+ " 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
};