fix iterate_matching_deposits(), LIMIT does not work with variables in Postgres (#4360)

This commit is contained in:
Christian Grothoff 2016-04-06 10:22:09 +02:00
parent f3819ae60d
commit ad8351c912
6 changed files with 110 additions and 20 deletions

View File

@ -78,9 +78,10 @@ static int test_mode;
* of the smallest possible unit are aggregated, they do surpass the * of the smallest possible unit are aggregated, they do surpass the
* "tiny" threshold beyond which we never trigger a wire transaction! * "tiny" threshold beyond which we never trigger a wire transaction!
* *
* TODO: make configurable (via config file or command line option) * Note: do not change here, Postgres requires us to hard-code the
* LIMIT in the prepared statement.
*/ */
static unsigned int aggregation_limit = 10000; static unsigned int aggregation_limit = TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT;
/** /**

View File

@ -80,6 +80,7 @@ test_exchangedb_postgres_SOURCES = \
test_exchangedb.c test_exchangedb.c
test_exchangedb_postgres_LDADD = \ test_exchangedb_postgres_LDADD = \
libtalerexchangedb.la \ libtalerexchangedb.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_srcdir)/src/util/libtalerutil.la \ $(top_srcdir)/src/util/libtalerutil.la \
$(top_srcdir)/src/pq/libtalerpq.la \ $(top_srcdir)/src/pq/libtalerpq.la \
-lgnunetutil -ljansson -lgnunetutil -ljansson

View File

@ -952,7 +952,7 @@ postgres_prepare (PGconn *db_conn)
" tiny=false AND" " tiny=false AND"
" done=false" " done=false"
" ORDER BY wire_deadline ASC" " ORDER BY wire_deadline ASC"
" LIMIT 1;", " LIMIT 1",
0, NULL); 0, NULL);
/* Used in #postgres_iterate_matching_deposits() */ /* Used in #postgres_iterate_matching_deposits() */
@ -975,8 +975,8 @@ postgres_prepare (PGconn *db_conn)
" h_wire=$2 AND" " h_wire=$2 AND"
" done=false" " done=false"
" ORDER BY wire_deadline ASC" " ORDER BY wire_deadline ASC"
" LIMIT $3", " LIMIT " TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR,
3, NULL); 2, NULL);
/* Used in #postgres_mark_deposit_tiny() */ /* Used in #postgres_mark_deposit_tiny() */
PREPARE ("mark_deposit_tiny", PREPARE ("mark_deposit_tiny",
@ -2336,7 +2336,6 @@ postgres_iterate_matching_deposits (void *cls,
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_auto_from_type (merchant_pub),
GNUNET_PQ_query_param_auto_from_type (h_wire), GNUNET_PQ_query_param_auto_from_type (h_wire),
GNUNET_PQ_query_param_uint32 (&limit),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
PGresult *result; PGresult *result;
@ -2344,8 +2343,8 @@ postgres_iterate_matching_deposits (void *cls,
unsigned int n; unsigned int n;
result = GNUNET_PQ_exec_prepared (session->conn, result = GNUNET_PQ_exec_prepared (session->conn,
"deposits_iterate_matching", "deposits_iterate_matching",
params); params);
if (PGRES_TUPLES_OK != if (PGRES_TUPLES_OK !=
PQresultStatus (result)) PQresultStatus (result))
{ {
@ -2366,28 +2365,25 @@ postgres_iterate_matching_deposits (void *cls,
struct TALER_Amount deposit_fee; struct TALER_Amount deposit_fee;
struct GNUNET_TIME_Absolute wire_deadline; struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_HashCode h_contract; struct GNUNET_HashCode h_contract;
struct TALER_MerchantPublicKeyP merchant_pub;
struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendPublicKeyP coin_pub;
uint64_t transaction_id; uint64_t transaction_id;
uint64_t serial_id; uint64_t serial_id;
int ret; int ret;
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("serial_id", GNUNET_PQ_result_spec_uint64 ("serial_id",
&serial_id), &serial_id),
GNUNET_PQ_result_spec_uint64 ("transaction_id", GNUNET_PQ_result_spec_uint64 ("transaction_id",
&transaction_id), &transaction_id),
TALER_PQ_result_spec_amount ("amount_with_fee", TALER_PQ_result_spec_amount ("amount_with_fee",
&amount_with_fee), &amount_with_fee),
TALER_PQ_result_spec_amount ("deposit_fee", TALER_PQ_result_spec_amount ("deposit_fee",
&deposit_fee), &deposit_fee),
GNUNET_PQ_result_spec_absolute_time ("wire_deadline", GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
&wire_deadline), &wire_deadline),
GNUNET_PQ_result_spec_auto_from_type ("h_contract", GNUNET_PQ_result_spec_auto_from_type ("h_contract",
&h_contract), &h_contract),
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
&merchant_pub),
GNUNET_PQ_result_spec_auto_from_type ("coin_pub", GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
&coin_pub), &coin_pub),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
if (GNUNET_OK != if (GNUNET_OK !=
@ -2399,7 +2395,7 @@ postgres_iterate_matching_deposits (void *cls,
} }
ret = deposit_cb (deposit_cb_cls, ret = deposit_cb (deposit_cb_cls,
serial_id, serial_id,
&merchant_pub, merchant_pub,
&coin_pub, &coin_pub,
&amount_with_fee, &amount_with_fee,
&deposit_fee, &deposit_fee,

View File

@ -20,6 +20,7 @@
*/ */
#include "platform.h" #include "platform.h"
#include "taler_exchangedb_lib.h" #include "taler_exchangedb_lib.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
static int result; static int result;
@ -546,6 +547,70 @@ cb_wtid_check (void *cls,
} }
/**
* Function called with details about deposits that
* have been made. Called in the test on the
* deposit given in @a cls.
*
* @param cls closure a `struct TALER_EXCHANGEDB_Deposit *`
* @param rowid unique ID for the deposit in our DB, used for marking
* it as 'tiny' or 'done'
* @param merchant_pub public key of the merchant
* @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
* @param deposit_fee amount the exchange gets to keep as transaction fees
* @param transaction_id unique transaction ID chosen by the merchant
* @param h_contract hash of the contract between merchant and customer
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param wire wire details for the merchant, NULL from iterate_matching_deposits()
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR if deposit does
* not match our expectations
*/
static int
deposit_cb (void *cls,
unsigned long long rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
uint64_t transaction_id,
const struct GNUNET_HashCode *h_contract,
struct GNUNET_TIME_Absolute wire_deadline,
const json_t *wire)
{
struct TALER_EXCHANGEDB_Deposit *deposit = cls;
struct GNUNET_HashCode h_wire;
if (NULL != wire)
TALER_JSON_hash (wire, &h_wire);
if ( (0 != memcmp (merchant_pub,
&deposit->merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP))) ||
(0 != TALER_amount_cmp (amount_with_fee,
&deposit->amount_with_fee)) ||
(0 != TALER_amount_cmp (deposit_fee,
&deposit->deposit_fee)) ||
(0 != memcmp (h_contract,
&deposit->h_contract,
sizeof (struct GNUNET_HashCode))) ||
(0 != memcmp (coin_pub,
&deposit->coin.coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP))) ||
(transaction_id != deposit->transaction_id) ||
( (NULL != wire) &&
(0 != memcmp (&h_wire,
&deposit->h_wire,
sizeof (struct GNUNET_HashCode))) ) )
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/** /**
* Main function that will be run by the scheduler. * Main function that will be run by the scheduler.
* *
@ -739,14 +804,16 @@ run (void *cls,
RND_BLK (&deposit.csig); RND_BLK (&deposit.csig);
RND_BLK (&deposit.merchant_pub); RND_BLK (&deposit.merchant_pub);
RND_BLK (&deposit.h_contract); RND_BLK (&deposit.h_contract);
RND_BLK (&deposit.h_wire);
wire = json_loads (json_wire_str, 0, NULL); wire = json_loads (json_wire_str, 0, NULL);
TALER_JSON_hash (wire,
&deposit.h_wire);
deposit.wire = wire; deposit.wire = wire;
deposit.transaction_id = deposit.transaction_id =
GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
deposit.amount_with_fee = value; deposit.amount_with_fee = value;
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (CURRENCY, &deposit.deposit_fee)); TALER_amount_get_zero (CURRENCY, &deposit.deposit_fee));
result = 8;
FAILIF (GNUNET_OK != FAILIF (GNUNET_OK !=
plugin->insert_deposit (plugin->cls, plugin->insert_deposit (plugin->cls,
session, &deposit)); session, &deposit));
@ -754,6 +821,15 @@ run (void *cls,
plugin->have_deposit (plugin->cls, plugin->have_deposit (plugin->cls,
session, session,
&deposit)); &deposit));
result = 9;
FAILIF (1 !=
plugin->iterate_matching_deposits (plugin->cls,
session,
&deposit.h_wire,
&deposit.merchant_pub,
&deposit_cb, &deposit,
2));
result = 10;
deposit2 = deposit; deposit2 = deposit;
deposit2.transaction_id++; /* should fail if transaction id is different */ deposit2.transaction_id++; /* should fail if transaction id is different */
FAILIF (GNUNET_NO != FAILIF (GNUNET_NO !=
@ -880,6 +956,9 @@ main (int argc,
GNUNET_break (0); GNUNET_break (0);
return -1; return -1;
} }
GNUNET_log_setup (argv[0],
"WARNING",
NULL);
plugin_name++; plugin_name++;
(void) GNUNET_asprintf (&testname, (void) GNUNET_asprintf (&testname,
"test-exchange-db-%s", plugin_name); "test-exchange-db-%s", plugin_name);

View File

@ -947,6 +947,17 @@ struct TALER_EXCHANGEDB_Plugin
void *deposit_cb_cls); void *deposit_cb_cls);
/**
* Maximum number of results we return from iterate_matching_deposits().
*
* Limit on the number of transactions we aggregate at once. Note
* that the limit must be big enough to ensure that when transactions
* of the smallest possible unit are aggregated, they do surpass the
* "tiny" threshold beyond which we never trigger a wire transaction!
*/
#define TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT 10000
#define TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR "10000"
/** /**
* Obtain information about other pending deposits for the same * Obtain information about other pending deposits for the same
* destination. Those deposits must not already be "done". * destination. Those deposits must not already be "done".
@ -957,7 +968,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param merchant_pub public key of the merchant * @param merchant_pub public key of the merchant
* @param deposit_cb function to call for each deposit * @param deposit_cb function to call for each deposit
* @param deposit_cb_cls closure for @a deposit_cb * @param deposit_cb_cls closure for @a deposit_cb
* @param limit maximum number of matching deposits to return * @param limit maximum number of matching deposits to return; should
* be #TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT, larger values
* are not supported, smaller values would be inefficient.
* @return number of rows processed, 0 if none exist, * @return number of rows processed, 0 if none exist,
* #GNUNET_SYSERR on error * #GNUNET_SYSERR on error
*/ */

View File

@ -89,7 +89,7 @@ TALER_JSON_spec_denomination_signature (const char *field,
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/ */
int int
TALER_JSON_hash (json_t *json, TALER_JSON_hash (const json_t *json,
struct GNUNET_HashCode *hc); struct GNUNET_HashCode *hc);
#endif /* TALER_JSON_LIB_H_ */ #endif /* TALER_JSON_LIB_H_ */