diff options
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 1422 | ||||
-rw-r--r-- | src/backenddb/test_merchantdb.c | 89 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 92 |
3 files changed, 787 insertions, 816 deletions
diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index 89d79045..827891ce 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -155,15 +155,9 @@ postgres_start (void *cls) } PQclear (result); return GNUNET_OK; - - /** - * NOTE: this function equals 99% the one from the exchange codebase. - * The difference is that here we don't use the session->state field ( - * see original code: exchange/src/exchangedb/plugin_exchangedb_postgres.c). - * Good/bad? - */ } + /** * Roll back the current transaction of a database connection. * @@ -188,31 +182,19 @@ postgres_rollback (void *cls) * Commit the current transaction of a database connection. * * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return #GNUNET_SYSERR on hard error, - * #GNUNET_NO if commit failed but retry may work, - * #GNUNET_OK on success + * @return transaction status code */ -static int +static enum GNUNET_DB_QueryStatus postgres_commit (void *cls) { struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus ret; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_end }; - ret = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "end_transaction", - params); - switch (ret) - { - case GNUNET_DB_STATUS_HARD_ERROR: - return GNUNET_SYSERR; - case GNUNET_DB_STATUS_SOFT_ERROR: - return GNUNET_NO; - default: - return GNUNET_OK; - } + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "end_transaction", + params); } @@ -235,6 +217,7 @@ postgres_initialize (void *cls) ",timestamp INT8 NOT NULL" ",row_id BIGSERIAL" ",PRIMARY KEY (order_id, merchant_pub)" + ",UNIQUE (h_contract_terms, merchant_pub)" ");"), GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS merchant_refunds (" " rtransaction_id BIGSERIAL" @@ -562,17 +545,15 @@ postgres_initialize (void *cls) * @param contract_terms where to store the retrieved proposal data * @param h_contract_terms proposal data's hashcode that will be used to * perform the lookup - * @return #GNUNET_OK on success, #GNUNET_NO if no proposal is - * found, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_contract_terms_from_hash (void *cls, json_t **contract_terms, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub) { struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus res; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (merchant_pub), @@ -584,24 +565,10 @@ postgres_find_contract_terms_from_hash (void *cls, GNUNET_PQ_result_spec_end }; - res = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "find_contract_terms_from_hash", - params, - rs); - if (res < 0) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (1 < res) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Mupltiple proposal data hash the same hashcode!\n"); - return GNUNET_SYSERR; - } - if (0 == res) - return GNUNET_NO; - return GNUNET_OK; + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "find_contract_terms_from_hash", + params, + rs); } @@ -611,17 +578,15 @@ postgres_find_contract_terms_from_hash (void *cls, * @param cls closure * @param[out] contract_terms where to store the retrieved contract terms * @param order id order id used to perform the lookup - * @return #GNUNET_OK on success, #GNUNET_NO if no proposal is - * found, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_contract_terms (void *cls, json_t **contract_terms, const char *order_id, const struct TALER_MerchantPublicKeyP *merchant_pub) { struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus res; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_auto_from_type (merchant_pub), @@ -638,24 +603,10 @@ postgres_find_contract_terms (void *cls, "Finding contract term, order_id: '%s', merchant_pub: '%s'.\n", order_id, TALER_B2S (merchant_pub)); - res = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "find_contract_terms", - params, - rs); - if (res < 0) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (res > 1) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Mupltiple proposal data share the same hashcode.\n"); - return GNUNET_SYSERR; - } - if (0 == res) - return GNUNET_NO; - return GNUNET_OK; + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "find_contract_terms", + params, + rs); } @@ -665,9 +616,9 @@ postgres_find_contract_terms (void *cls, * @param cls closure * @param order_id identificator of the proposal being stored * @param contract_terms proposal data to store - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_insert_contract_terms (void *cls, const char *order_id, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -675,20 +626,7 @@ postgres_insert_contract_terms (void *cls, const json_t *contract_terms) { struct PostgresClosure *pg = cls; - PGresult *result; - int ret; struct GNUNET_HashCode h_contract_terms; - - if (GNUNET_OK != TALER_JSON_hash (contract_terms, - &h_contract_terms)) - return GNUNET_SYSERR; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "inserting contract_terms: order_id: %s, merchant_pub: %s, h_contract_terms: %s.\n", - order_id, - TALER_B2S (merchant_pub), - GNUNET_h2s (&h_contract_terms)); - struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_auto_from_type (merchant_pub), @@ -697,33 +635,22 @@ postgres_insert_contract_terms (void *cls, GNUNET_PQ_query_param_auto_from_type (&h_contract_terms), GNUNET_PQ_query_param_end }; - - result = GNUNET_PQ_exec_prepared (pg->conn, - "insert_contract_terms", - params); - - /** - * We don't treat a unique_violation (code '23505') error as - * an actual error, since there is no problem if a frontend tries - * to store twice the same proposal. That is especially needed - * when DB-less frontends perform replayed payments. - */ - if (PGRES_COMMAND_OK != PQresultStatus (result) - && (0 != memcmp ("23505", - EXTRACT_DB_ERROR (result), - 5))) - { - ret = GNUNET_SYSERR; - BREAK_DB_ERR (result); - } - else - { - ret = GNUNET_OK; - } - PQclear (result); - return ret; + + if (GNUNET_OK != + TALER_JSON_hash (contract_terms, + &h_contract_terms)) + return GNUNET_SYSERR; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "inserting contract_terms: order_id: %s, merchant_pub: %s, h_contract_terms: %s.\n", + order_id, + TALER_B2S (merchant_pub), + GNUNET_h2s (&h_contract_terms)); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_contract_terms", + params); } + /** * Insert transaction data into the database. * @@ -736,9 +663,9 @@ postgres_insert_contract_terms (void *cls, * @param timestamp time of the confirmation * @param refund refund deadline * @param total_amount total amount we receive for the contract after fees - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_store_transaction (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -749,9 +676,6 @@ postgres_store_transaction (void *cls, const struct TALER_Amount *total_amount) { struct PostgresClosure *pg = cls; - PGresult *result; - int ret; - struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_string (exchange_uri), @@ -767,21 +691,9 @@ postgres_store_transaction (void *cls, "Storing transaction with h_contract_terms '%s', merchant_pub '%s'.\n", GNUNET_h2s (h_contract_terms), TALER_B2S (merchant_pub)); - - result = GNUNET_PQ_exec_prepared (pg->conn, - "insert_transaction", - params); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - ret = GNUNET_SYSERR; - BREAK_DB_ERR (result); - } - else - { - ret = GNUNET_OK; - } - PQclear (result); - return ret; + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_transaction", + params); } @@ -797,9 +709,9 @@ postgres_store_transaction (void *cls, * @param refund_fee fee the exchange will charge for refunding this coin * @param signkey_pub public key used by the exchange for @a exchange_proof * @param exchange_proof proof from exchange that coin was accepted - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_store_deposit (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -811,8 +723,6 @@ postgres_store_deposit (void *cls, const json_t *exchange_proof) { struct PostgresClosure *pg = cls; - PGresult *result; - int ret; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (merchant_pub), @@ -832,20 +742,9 @@ postgres_store_deposit (void *cls, TALER_B2S (coin_pub), TALER_amount_to_string (amount_with_fee), TALER_B2S (merchant_pub)); - result = GNUNET_PQ_exec_prepared (pg->conn, - "insert_deposit", - params); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - ret = GNUNET_SYSERR; - BREAK_DB_ERR (result); - } - else - { - ret = GNUNET_OK; - } - PQclear (result); - return ret; + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_deposit", + params); } @@ -858,18 +757,15 @@ postgres_store_deposit (void *cls, * @param coin_pub public key of the coin * @param wtid identifier of the wire transfer in which the exchange * send us the money for the coin deposit - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_store_coin_to_transfer (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_WireTransferIdentifierRawP *wtid) { struct PostgresClosure *pg = cls; - PGresult *result; - int ret; - struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (coin_pub), @@ -877,20 +773,9 @@ postgres_store_coin_to_transfer (void *cls, GNUNET_PQ_query_param_end }; - result = GNUNET_PQ_exec_prepared (pg->conn, - "insert_transfer", - params); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - ret = GNUNET_SYSERR; - BREAK_DB_ERR (result); - } - else - { - ret = GNUNET_OK; - } - PQclear (result); - return ret; + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_transfer", + params); } @@ -903,9 +788,9 @@ postgres_store_coin_to_transfer (void *cls, * @param execution_time when was @a wtid executed * @param signkey_pub public key used by the exchange for @a exchange_proof * @param exchange_proof proof from exchange about what the deposit was for - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_store_transfer_to_proof (void *cls, const char *exchange_uri, const struct TALER_WireTransferIdentifierRawP *wtid, @@ -914,9 +799,6 @@ postgres_store_transfer_to_proof (void *cls, const json_t *exchange_proof) { struct PostgresClosure *pg = cls; - PGresult *result; - int ret; - struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (exchange_uri), GNUNET_PQ_query_param_auto_from_type (wtid), @@ -926,22 +808,12 @@ postgres_store_transfer_to_proof (void *cls, GNUNET_PQ_query_param_end }; - result = GNUNET_PQ_exec_prepared (pg->conn, - "insert_proof", - params); - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - ret = GNUNET_SYSERR; - BREAK_DB_ERR (result); - } - else - { - ret = GNUNET_OK; - } - PQclear (result); - return ret; + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_proof", + params); } + /** * Lookup for a proposal, respecting the signature used by the * /history's db methods. @@ -951,10 +823,9 @@ postgres_store_transfer_to_proof (void *cls, * @param merchant_pub public key of the merchant using this method * @param cb the callback * @param cb_cls closure to pass to the callback - * @return GNUNET_YES, GNUNET_NO, GNUNET_SYSERR according to the - * query being successful, unsuccessful, or generated errors. + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_contract_terms_history (void *cls, const char *order_id, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -962,53 +833,102 @@ postgres_find_contract_terms_history (void *cls, void *cb_cls) { struct PostgresClosure *pg = cls; - PGresult *result; - unsigned int i; json_t *contract_terms; - + enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; - - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_contract_terms", - params); - i = PQntuples (result); - if (1 < i) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Mupltiple proposal data share the same hashcode.\n"); - return GNUNET_SYSERR; - } - - if (0 == i) - return GNUNET_NO; - struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_result_spec_json ("contract_terms", &contract_terms), GNUNET_PQ_result_spec_end }; - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - 0)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "find_contract_terms", + params, + rs); + if (qs <= 0) + return qs; cb (cb_cls, order_id, 0, contract_terms); + GNUNET_PQ_cleanup_result (rs); + return qs; +} - PQclear (result); - return GNUNET_OK; +/** + * Closure for #find_contracts_cb(). + */ +struct FindContractsContext +{ + /** + * Function to call on each result. + */ + TALER_MERCHANTDB_ProposalDataCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Transaction status code to set. + */ + enum GNUNET_DB_QueryStatus qs; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls of type `struct FindContractsContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +find_contracts_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct FindContractsContext *fcctx = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + char *order_id; + json_t *contract_terms; + uint64_t row_id; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("order_id", + &order_id), + TALER_PQ_result_spec_json ("contract_terms", + &contract_terms), + GNUNET_PQ_result_spec_uint64 ("row_id", + &row_id), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + fcctx->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; + } + fcctx->qs = i + 1; + fcctx->cb (fcctx->cb_cls, + order_id, + row_id, + contract_terms); + GNUNET_PQ_cleanup_result (rs); + } } @@ -1035,9 +955,9 @@ postgres_find_contract_terms_history (void *cls, * Web interface. * @param cb function to call with transaction data, can be NULL. * @param cb_cls closure for @a cb - * @return numer of found tuples, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_contract_terms_by_date_and_range (void *cls, struct GNUNET_TIME_Absolute date, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -1050,8 +970,6 @@ postgres_find_contract_terms_by_date_and_range (void *cls, uint64_t s64 = start; uint64_t r64 = nrows; struct PostgresClosure *pg = cls; - PGresult *result; - unsigned int n; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_absolute_time (&date), GNUNET_PQ_query_param_auto_from_type (merchant_pub), @@ -1059,63 +977,25 @@ postgres_find_contract_terms_by_date_and_range (void *cls, GNUNET_PQ_query_param_uint64 (&r64), GNUNET_PQ_query_param_end }; + const char *stmt; + enum GNUNET_DB_QueryStatus qs; + struct FindContractsContext fcctx = { + .cb = cb, + .cb_cls = cb_cls + }; if (GNUNET_YES == future) - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_contract_terms_by_date_and_range_future", - params); + stmt = "find_contract_terms_by_date_and_range_future"; else - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_contract_terms_by_date_and_range", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if ( (0 == (n = PQntuples (result))) || - (NULL == cb) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No records found.\n"); - PQclear (result); - return n; - } - for (unsigned int i = 0; i < n; i++) - { - char *order_id; - json_t *contract_terms; - uint64_t row_id; - - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("order_id", - &order_id), - TALER_PQ_result_spec_json ("contract_terms", - &contract_terms), - GNUNET_PQ_result_spec_uint64 ("row_id", - &row_id), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - cb (cb_cls, - order_id, - row_id, - contract_terms); - - GNUNET_PQ_cleanup_result (rs); - } - PQclear (result); - return n; + stmt = "find_contract_terms_by_date_and_range"; + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + stmt, + params, + &find_contracts_cb, + &fcctx); + if (0 >= qs) + return qs; + return fcctx.qs; } @@ -1130,79 +1010,41 @@ postgres_find_contract_terms_by_date_and_range (void *cls, * @param nrows at most nrows rows are returned. * @param cb function to call with transaction data, can be NULL. * @param cb_cls closure for @a cb - * @return numer of found tuples, #GNUNET_SYSERR upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_contract_terms_by_date (void *cls, - struct GNUNET_TIME_Absolute date, - const struct TALER_MerchantPublicKeyP *merchant_pub, - unsigned int nrows, - TALER_MERCHANTDB_ProposalDataCallback cb, - void *cb_cls) + struct GNUNET_TIME_Absolute date, + const struct TALER_MerchantPublicKeyP *merchant_pub, + unsigned int nrows, + TALER_MERCHANTDB_ProposalDataCallback cb, + void *cb_cls) { - - uint64_t r64 = nrows; struct PostgresClosure *pg = cls; - PGresult *result; - unsigned int n; - unsigned int i; - + uint64_t r64 = nrows; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_absolute_time (&date), GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_uint64 (&r64), GNUNET_PQ_query_param_end }; - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_contract_terms_by_date", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == (n = PQntuples (result)) || NULL == cb) - { - PQclear (result); - return n; - } - for (i = 0; i < n; i++) - { - char *order_id; - json_t *contract_terms; - uint64_t row_id; - - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("order_id", - &order_id), - TALER_PQ_result_spec_json ("contract_terms", - &contract_terms), - GNUNET_PQ_result_spec_uint64 ("row_id", - &row_id), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - cb (cb_cls, - order_id, - row_id, - contract_terms); + enum GNUNET_DB_QueryStatus qs; + struct FindContractsContext fcctx = { + .cb = cb, + .cb_cls = cb_cls + }; - GNUNET_PQ_cleanup_result (rs); - } - PQclear (result); - return n; + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "find_contract_terms_by_date", + params, + &find_contracts_cb, + &fcctx); + if (0 >= qs) + return qs; + return fcctx.qs; } + /** * Find information about a transaction. * @@ -1211,10 +1053,9 @@ postgres_find_contract_terms_by_date (void *cls, * @param merchant_pub merchant's public key * @param cb function to call with transaction data * @param cb_cls closure for @a cb - * @return #GNUNET_OK if found, #GNUNET_NO if not, #GNUNET_SYSERR - * upon error + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_transaction (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -1222,84 +1063,138 @@ postgres_find_transaction (void *cls, void *cb_cls) { struct PostgresClosure *pg = cls; - PGresult *result; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; + char *exchange_uri; + struct GNUNET_HashCode h_wire; + struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_TIME_Absolute refund_deadline; + struct TALER_Amount total_amount; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_string ("exchange_uri", + &exchange_uri), + GNUNET_PQ_result_spec_auto_from_type ("h_wire", + &h_wire), + GNUNET_PQ_result_spec_absolute_time ("timestamp", + ×tamp), + GNUNET_PQ_result_spec_absolute_time ("refund_deadline", + &refund_deadline), + TALER_PQ_result_spec_amount ("total_amount", + &total_amount), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finding transaction for h_contract_terms '%s', merchant_pub: '%s'.\n", GNUNET_h2s (h_contract_terms), TALER_B2S (merchant_pub)); - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_transaction", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "find_transaction", + params, + rs); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; + cb (cb_cls, + merchant_pub, + exchange_uri, + h_contract_terms, + &h_wire, + timestamp, + refund_deadline, + &total_amount); + GNUNET_PQ_cleanup_result (rs); } - if (0 == PQntuples (result)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Could NOT find transaction for h_contract_terms '%s'\n", - GNUNET_h2s (h_contract_terms)); + return qs; +} - PQclear (result); - return GNUNET_NO; - } - if (1 != PQntuples (result)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } +/** + * Closure for #find_payments_cb(). + */ +struct FindPaymentsContext +{ + /** + * Function to call with results. + */ + TALER_MERCHANTDB_CoinDepositCallback cb; + + /** + * Closure for @e cls. + */ + void *cb_cls; + + /** + * Contract term hash used for the search. + */ + const struct GNUNET_HashCode *h_contract_terms; + + /** + * Transaction status (set). + */ + enum GNUNET_DB_QueryStatus qs; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls of type `struct FindPaymentsContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +find_payments_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct FindPaymentsContext *fpc = cls; + + for (unsigned int i=0;i<num_results;i++) { - char *exchange_uri; - struct GNUNET_HashCode h_wire; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_TIME_Absolute refund_deadline; - struct TALER_Amount total_amount; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_Amount amount_with_fee; + struct TALER_Amount deposit_fee; + struct TALER_Amount refund_fee; + json_t *exchange_proof; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("exchange_uri", - &exchange_uri), - GNUNET_PQ_result_spec_auto_from_type ("h_wire", - &h_wire), - GNUNET_PQ_result_spec_absolute_time ("timestamp", - ×tamp), - GNUNET_PQ_result_spec_absolute_time ("refund_deadline", - &refund_deadline), - TALER_PQ_result_spec_amount ("total_amount", - &total_amount), + GNUNET_PQ_result_spec_auto_from_type ("coin_pub", + &coin_pub), + TALER_PQ_result_spec_amount ("amount_with_fee", + &amount_with_fee), + TALER_PQ_result_spec_amount ("deposit_fee", + &deposit_fee), + TALER_PQ_result_spec_amount ("refund_fee", + &refund_fee), + TALER_PQ_result_spec_json ("exchange_proof", + &exchange_proof), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != GNUNET_PQ_extract_result (result, rs, - 0)) + i)) { GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; + fpc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; } - cb (cb_cls, - merchant_pub, - exchange_uri, - h_contract_terms, - &h_wire, - timestamp, - refund_deadline, - &total_amount); + fpc->qs = i + 1; + fpc->cb (fpc->cb_cls, + fpc->h_contract_terms, + &coin_pub, + &amount_with_fee, + &deposit_fee, + &refund_fee, + exchange_proof); GNUNET_PQ_cleanup_result (rs); } - PQclear (result); - return GNUNET_OK; } @@ -1312,10 +1207,9 @@ postgres_find_transaction (void *cls, * @param merchant_pub merchant's public key * @param cb function to call with payment data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_payments (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -1323,43 +1217,86 @@ postgres_find_payments (void *cls, void *cb_cls) { struct PostgresClosure *pg = cls; - PGresult *result; - unsigned int i; - struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; + struct FindPaymentsContext fpc = { + .h_contract_terms = h_contract_terms, + .cb = cb, + .cb_cls = cb_cls + }; + enum GNUNET_DB_QueryStatus qs; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "finding payment for h_contract_terms '%s'\n", + "Finding payment for h_contract_terms '%s'\n", GNUNET_h2s (h_contract_terms)); - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_deposits", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "find_deposits", + params, + &find_payments_cb, + &fpc); + if (qs <= 0) + return qs; + return fpc.qs; +} + + +/** + * Closure for #find_payments_by_coin_cb(). + */ +struct FindPaymentsByCoinContext +{ + /** + * Function to call with results. + */ + TALER_MERCHANTDB_CoinDepositCallback cb; + + /** + * Closure for @e cls. + */ + void *cb_cls; + + /** + * Coin we are looking for. + */ + const struct TALER_CoinSpendPublicKeyP *coin_pub; + + /** + * Hash of the contract we are looking for. + */ + const struct GNUNET_HashCode *h_contract_terms; + + /** + * Transaction status (set). + */ + enum GNUNET_DB_QueryStatus qs; +}; - for (i=0;i<PQntuples (result);i++) + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls of type `struct FindPaymentsByCoinContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +find_payments_by_coin_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct FindPaymentsByCoinContext *fpc = cls; + + for (unsigned int i=0;i<num_results;i++) { - struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_Amount amount_with_fee; struct TALER_Amount deposit_fee; struct TALER_Amount refund_fee; json_t *exchange_proof; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &coin_pub), TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee), TALER_PQ_result_spec_amount ("deposit_fee", @@ -1377,23 +1314,19 @@ postgres_find_payments (void *cls, i)) { GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; + fpc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; } - cb (cb_cls, - h_contract_terms, - &coin_pub, - &amount_with_fee, - &deposit_fee, - &refund_fee, - exchange_proof); + fpc->qs = i + 1; + fpc->cb (fpc->cb_cls, + fpc->h_contract_terms, + fpc->coin_pub, + &amount_with_fee, + &deposit_fee, + &refund_fee, + exchange_proof); GNUNET_PQ_cleanup_result (rs); } - PQclear (result); - return GNUNET_OK; - - GNUNET_break (0); - return GNUNET_SYSERR; } @@ -1406,10 +1339,9 @@ postgres_find_payments (void *cls, * @param coin_pub coin's public key used for the search * @param cb function to call with payment data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_payments_by_hash_and_coin (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -1418,120 +1350,74 @@ postgres_find_payments_by_hash_and_coin (void *cls, void *cb_cls) { struct PostgresClosure *pg = cls; - PGresult *result; - unsigned int i; - struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_auto_from_type (coin_pub), GNUNET_PQ_query_param_end }; + struct FindPaymentsByCoinContext fpc = { + .cb = cb, + .cb_cls = cb_cls, + .h_contract_terms = h_contract_terms, + .coin_pub = coin_pub + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "find_deposits_by_hash_and_coin", + params, + &find_payments_by_coin_cb, + &fpc); + if (0 >= qs) + return qs; + return fpc.qs; +} - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_deposits_by_hash_and_coin", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } - - for (i=0;i<PQntuples (result);i++) - { - struct TALER_Amount amount_with_fee; - struct TALER_Amount deposit_fee; - struct TALER_Amount refund_fee; - json_t *exchange_proof; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_amount ("amount_with_fee", - &amount_with_fee), - TALER_PQ_result_spec_amount ("deposit_fee", - &deposit_fee), - TALER_PQ_result_spec_amount ("refund_fee", - &refund_fee), - TALER_PQ_result_spec_json ("exchange_proof", - &exchange_proof), - GNUNET_PQ_result_spec_end - }; +/** + * Closure for #find_transfers_cb(). + */ +struct FindTransfersContext +{ + /** + * Function to call on results. + */ + TALER_MERCHANTDB_TransferCallback cb; - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - cb (cb_cls, - h_contract_terms, - coin_pub, - &amount_with_fee, - &deposit_fee, - &refund_fee, - exchange_proof); - GNUNET_PQ_cleanup_result (rs); - } - PQclear (result); - return GNUNET_OK; + /** + * Closure for @e cb. + */ + void *cb_cls; - GNUNET_break (0); - return GNUNET_SYSERR; -} + /** + * Hash of the contract we are looking under. + */ + const struct GNUNET_HashCode *h_contract_terms; + + /** + * Transaction status (set). + */ + enum GNUNET_DB_QueryStatus qs; +}; /** - * Lookup information about a transfer by @a h_contract_terms. Note - * that in theory there could be multiple wire transfers for a - * single @a h_contract_terms, as the transaction may have involved - * multiple coins and the coins may be spread over different wire - * transfers. + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. * - * @param cls closure - * @param h_contract_terms key for the search - * @param cb function to call with transfer data - * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown, - * #GNUNET_SYSERR on hard errors + * @param cls of type `struct FindTransfersContext *` + * @param result the postgres result + * @param num_result the number of results in @a result */ -static int -postgres_find_transfers_by_hash (void *cls, - const struct GNUNET_HashCode *h_contract_terms, - TALER_MERCHANTDB_TransferCallback cb, - void *cb_cls) +static void +find_transfers_cb (void *cls, + PGresult *result, + unsigned int num_results) { - struct PostgresClosure *pg = cls; - PGresult *result; - unsigned int i; - - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), - GNUNET_PQ_query_param_end - }; - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_transfers_by_hash", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } + struct FindTransfersContext *ftc = cls; - for (i=0;i<PQntuples (result);i++) + for (unsigned int i=0;i<PQntuples (result);i++) { struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_WireTransferIdentifierRawP wtid; @@ -1556,62 +1442,102 @@ postgres_find_transfers_by_hash (void *cls, i)) { GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; + ftc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; } - cb (cb_cls, - h_contract_terms, - &coin_pub, - &wtid, - execution_time, - proof); + ftc->qs = i + 1; + ftc->cb (ftc->cb_cls, + ftc->h_contract_terms, + &coin_pub, + &wtid, + execution_time, + proof); GNUNET_PQ_cleanup_result (rs); } - PQclear (result); - return GNUNET_OK; } - + /** - * Lookup information about a coin deposits by @a wtid. + * Lookup information about a transfer by @a h_contract_terms. Note + * that in theory there could be multiple wire transfers for a + * single @a h_contract_terms, as the transaction may have involved + * multiple coins and the coins may be spread over different wire + * transfers. * * @param cls closure - * @param wtid wire transfer identifier to find matching transactions for - * @param cb function to call with payment data + * @param h_contract_terms key for the search + * @param cb function to call with transfer data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ -static int -postgres_find_deposits_by_wtid (void *cls, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_MERCHANTDB_CoinDepositCallback cb, - void *cb_cls) +static enum GNUNET_DB_QueryStatus +postgres_find_transfers_by_hash (void *cls, + const struct GNUNET_HashCode *h_contract_terms, + TALER_MERCHANTDB_TransferCallback cb, + void *cb_cls) { struct PostgresClosure *pg = cls; - PGresult *result; - unsigned int i; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (wtid), + GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_end }; - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_deposits_by_wtid", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } + struct FindTransfersContext ftc = { + .h_contract_terms = h_contract_terms, + .cb = cb, + .cb_cls = cb_cls + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "find_transfers_by_hash", + params, + &find_transfers_cb, + &ftc); + if (0 >= qs) + return qs; + return ftc.qs; +} + + +/** + * Closure for #find_deposits_cb(). + */ +struct FindDepositsContext +{ - for (i=0;i<PQntuples (result);i++) + /** + * Function to call for each result. + */ + TALER_MERCHANTDB_CoinDepositCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Transaction status (set). + */ + enum GNUNET_DB_QueryStatus qs; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls of type `struct FindDepositsContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +find_deposits_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct FindDepositsContext *fdc = cls; + + for (unsigned int i=0;i<PQntuples (result);i++) { struct GNUNET_HashCode h_contract_terms; struct TALER_CoinSpendPublicKeyP coin_pub; @@ -1619,7 +1545,6 @@ postgres_find_deposits_by_wtid (void *cls, struct TALER_Amount deposit_fee; struct TALER_Amount refund_fee; json_t *exchange_proof; - struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", &h_contract_terms), @@ -1642,75 +1567,103 @@ postgres_find_deposits_by_wtid (void *cls, i)) { GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; + fdc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; } - cb (cb_cls, - &h_contract_terms, - &coin_pub, - &amount_with_fee, - &deposit_fee, - &refund_fee, - exchange_proof); + fdc->qs = i + 1; + fdc->cb (fdc->cb_cls, + &h_contract_terms, + &coin_pub, + &amount_with_fee, + &deposit_fee, + &refund_fee, + exchange_proof); GNUNET_PQ_cleanup_result (rs); } - PQclear (result); - return GNUNET_OK; } + /** - * Obtain refunds associated with a contract. + * Lookup information about a coin deposits by @a wtid. * - * @param cls closure, typically a connection to the db - * @param merchant_pub public key of the merchant instance - * @param h_contract_terms hash code of the contract - * @param rc function to call for each coin on which there is a refund - * @param rc_cls closure for @a rc - * @return #GNUNET_OK if we called @a rc on all coins - * #GNUNET_NO if there are no refunds for @a h_contract_terms - * #GNUNET_SYSERR if there were errors talking to the DB + * @param cls closure + * @param wtid wire transfer identifier to find matching transactions for + * @param cb function to call with payment data + * @param cb_cls closure for @a cb + * @return transaction status */ -int -postgres_get_refunds_from_contract_terms_hash (void *cls, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct GNUNET_HashCode *h_contract_terms, - TALER_MERCHANTDB_RefundCallback rc, - void *rc_cls) +static enum GNUNET_DB_QueryStatus +postgres_find_deposits_by_wtid (void *cls, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_MERCHANTDB_CoinDepositCallback cb, + void *cb_cls) { - struct PostgresClosure *pg = cls; - PGresult *result; - unsigned int i; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (merchant_pub), - GNUNET_PQ_query_param_auto_from_type (h_contract_terms), + GNUNET_PQ_query_param_auto_from_type (wtid), GNUNET_PQ_query_param_end }; + struct FindDepositsContext fdc = { + .cb = cb, + .cb_cls = cb_cls + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "find_deposits_by_wtid", + params, + &find_deposits_cb, + &fdc); + if (0 >= qs) + return qs; + return fdc.qs; +} + + +/** + * Closure for #get_refunds_cb(). + */ +struct GetRefundsContext +{ + /** + * Function to call for each refund. + */ + TALER_MERCHANTDB_RefundCallback rc; + + /** + * Closure for @e rc. + */ + void *rc_cls; + + /** + * Transaction result. + */ + enum GNUNET_DB_QueryStatus qs; +}; - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_refunds_from_contract_terms_hash", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } - for (i=0;i<PQntuples (result);i++) +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls of type `struct GetRefundsContext *` + * @param result the postgres result + * @param num_result the number of results in @a result + */ +static void +get_refunds_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct GetRefundsContext *grc = cls; + + for (unsigned int i=0;i<num_results;i++) { struct TALER_CoinSpendPublicKeyP coin_pub; uint64_t rtransaction_id; struct TALER_Amount refund_amount; struct TALER_Amount refund_fee; char *reason; - struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub), @@ -1734,20 +1687,57 @@ postgres_get_refunds_from_contract_terms_hash (void *cls, i)) { GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; + grc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; } - rc (rc_cls, - &coin_pub, - rtransaction_id, - reason, - &refund_amount, - &refund_fee); - + grc->rc (grc->rc_cls, + &coin_pub, + rtransaction_id, + reason, + &refund_amount, + &refund_fee); GNUNET_PQ_cleanup_result (rs); } - PQclear (result); - return GNUNET_OK; +} + + +/** + * Obtain refunds associated with a contract. + * + * @param cls closure, typically a connection to the db + * @param merchant_pub public key of the merchant instance + * @param h_contract_terms hash code of the contract + * @param rc function to call for each coin on which there is a refund + * @param rc_cls closure for @a rc + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +postgres_get_refunds_from_contract_terms_hash (void *cls, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_contract_terms, + TALER_MERCHANTDB_RefundCallback rc, + void *rc_cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (merchant_pub), + GNUNET_PQ_query_param_auto_from_type (h_contract_terms), + GNUNET_PQ_query_param_end + }; + struct GetRefundsContext grc = { + .rc = rc, + .rc_cls = rc_cls + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "find_refunds_from_contract_terms_hash", + params, + &get_refunds_cb, + &grc); + if (0 >= qs) + return qs; + return grc.qs; } @@ -1756,8 +1746,14 @@ postgres_get_refunds_from_contract_terms_hash (void *cls, */ struct FindRefundContext { + /** + * + */ struct TALER_Amount refunded_amount; + /** + * + */ int err; }; @@ -1791,8 +1787,10 @@ process_refund_cb (void *cls, GNUNET_PQ_extract_result (result, rs, i)) + { ictx->err = GNUNET_SYSERR; - + return; + } if (GNUNET_SYSERR == TALER_amount_add (&ictx->refunded_amount, &ictx->refunded_amount, @@ -1801,6 +1799,7 @@ process_refund_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not add amounts\n"); ictx->err = GNUNET_SYSERR; + return; } } } @@ -1865,7 +1864,6 @@ insert_refund (void *cls, const struct TALER_Amount *refund_fee) { struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_auto_from_type (h_contract_terms), @@ -1959,9 +1957,10 @@ process_deposits_cb (void *cls, } /*How much coin i will give for refund: needed by merchant_refunds table*/ - if (GNUNET_SYSERR == TALER_amount_subtract (&diff, // to commit as refund - &amount_with_fee, - &ictx.refunded_amount)) + if (GNUNET_SYSERR == + TALER_amount_subtract (&diff, // to commit as refund + &amount_with_fee, + &ictx.refunded_amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not compute refundable amount out of coin (%s)," @@ -2003,9 +2002,10 @@ process_deposits_cb (void *cls, } /*Subtract from refund what has already been awarded*/ - if (GNUNET_SYSERR == TALER_amount_subtract (big, - big, - small)) + if (GNUNET_SYSERR == + TALER_amount_subtract (big, + big, + small)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not subtract previous refund amount, attempted operation was:" @@ -2029,9 +2029,10 @@ process_deposits_cb (void *cls, big = &diff; small = ctx->refund; } - if (GNUNET_SYSERR == TALER_amount_subtract (big, - big, - small)) + if (GNUNET_SYSERR == + TALER_amount_subtract (big, + big, + small)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not subtract refund amount, attempted operation was:" @@ -2042,7 +2043,9 @@ process_deposits_cb (void *cls, /*Always commit the smallest as refund*/ - if ( (0 != small->value) || (0 != small->fraction) ) + if ( (0 != small->value) || + (0 != small->fraction) ) + { if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != insert_refund (ctx->pg, ctx->merchant_pub, @@ -2051,18 +2054,18 @@ process_deposits_cb (void *cls, ctx->reason, small, &refund_fee)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not commit refund worth %s for coin '%s'" - ", as of contract/reason: %s/%s\n", - TALER_amount_to_string (small), - TALER_B2S (&coin_pub), - GNUNET_h2s (ctx->h_contract_terms), - ctx->reason); - ctx->err = GNUNET_SYSERR; - return; - } - + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not commit refund worth %s for coin '%s'" + ", as of contract/reason: %s/%s\n", + TALER_amount_to_string (small), + TALER_B2S (&coin_pub), + GNUNET_h2s (ctx->h_contract_terms), + ctx->reason); + ctx->err = GNUNET_SYSERR; + return; + } + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Detracting %s as refund, from coin %s\n", TALER_amount_to_string (small), @@ -2070,7 +2073,6 @@ process_deposits_cb (void *cls, small->value = 0; small->fraction = 0; - if ( (0 == ctx->refund->value) && (0 == ctx->refund->fraction) ) { @@ -2078,7 +2080,6 @@ process_deposits_cb (void *cls, "All refund amount has been allocated\n"); break; } - } /** @@ -2118,16 +2119,14 @@ process_deposits_cb (void *cls, ctx->err = GNUNET_OK; return; - rollback: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed transaction, doing rollback\n"); - PQclear (result); - postgres_rollback (ctx->pg); - return; + rollback: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed transaction, doing rollback\n"); + PQclear (result); + postgres_rollback (ctx->pg); } - /** * Function called when some backoffice staff decides to award or * increase the refund on an existing contract. @@ -2138,14 +2137,13 @@ process_deposits_cb (void *cls, * @param refund maximum refund to return to the customer for this contract * @param reason 0-terminated UTF-8 string giving the reason why the customer * got a refund (free form, business-specific) - * @return #GNUNET_OK if the refund is accepted - * #GNUNET_NO if the refund cannot be issued: this can happen for two + * @return transaction status + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the refund is accepted + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the refund cannot be issued: this can happen for two * reasons: the issued refund is not greater of the previous refund, * or the coins don't have enough amount left to pay for this refund. - * #GNUNET_SYSERR on database error, i.e. contract unknown, DB on fire, - * (FIXME: distinguish hard/soft? who does retries?) */ -static int +static enum GNUNET_DB_QueryStatus postgres_increase_refund_for_contract (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -2154,7 +2152,7 @@ postgres_increase_refund_for_contract (void *cls, { struct PostgresClosure *pg = cls; struct InsertRefundContext ctx; - enum GNUNET_DB_QueryStatus ret; + enum GNUNET_DB_QueryStatus qs; struct TALER_Amount _refund = *refund; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (h_contract_terms), @@ -2166,44 +2164,43 @@ postgres_increase_refund_for_contract (void *cls, postgres_start (cls)) { GNUNET_break (0); - return GNUNET_SYSERR; + return GNUNET_DB_STATUS_HARD_ERROR; } - ctx.pg = pg; ctx.err = GNUNET_OK; ctx.refund = &_refund; ctx.reason = reason; ctx.h_contract_terms = h_contract_terms; ctx.merchant_pub = merchant_pub; - - ret = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "find_deposits", - params, - &process_deposits_cb, - &ctx); - - switch (ret) + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "find_deposits", + params, + &process_deposits_cb, + &ctx); + switch (qs) { case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown contract: %s (merchant_pub: %s), no refund possible\n", GNUNET_h2s (h_contract_terms), TALER_B2S (merchant_pub)); - return GNUNET_SYSERR; + return qs; case GNUNET_DB_STATUS_SOFT_ERROR: - return GNUNET_SYSERR; /* UGH, BUG #5010! */ + return qs; case GNUNET_DB_STATUS_HARD_ERROR: - return GNUNET_SYSERR; + return qs; default: /* Got one or more deposits */ - if (GNUNET_OK != postgres_commit (cls)) + qs = postgres_commit (cls); + if (0 > qs) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to commit transaction increasing refund\n"); - return GNUNET_SYSERR; + return qs; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Committed db transaction\n"); - return ctx.err; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Committed db transaction\n"); + return qs; } } @@ -2216,10 +2213,9 @@ postgres_increase_refund_for_contract (void *cls, * @param wtid wire transfer identifier for the search * @param cb function to call with proof data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if transaction Id is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ -static int +static enum GNUNET_DB_QueryStatus postgres_find_proof_by_wtid (void *cls, const char *exchange_uri, const struct TALER_WireTransferIdentifierRawP *wtid, @@ -2227,58 +2223,30 @@ postgres_find_proof_by_wtid (void *cls, void *cb_cls) { struct PostgresClosure *pg = cls; - PGresult *result; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (wtid), GNUNET_PQ_query_param_string (exchange_uri), GNUNET_PQ_query_param_end }; + json_t *proof; + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_json ("proof", + &proof), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; - result = GNUNET_PQ_exec_prepared (pg->conn, - "find_proof_by_wtid", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result); - PQclear (result); - return GNUNET_SYSERR; - } - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } - if (1 != PQntuples (result)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "find_proof_by_wtid", + params, + rs); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) { - json_t *proof; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_json ("proof", - &proof), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - 0)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } cb (cb_cls, proof); GNUNET_PQ_cleanup_result (rs); } - - PQclear (result); - return GNUNET_OK; + return qs; } diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index c90691d9..13d78628 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -182,6 +182,11 @@ static json_t *contract; */ static json_t *contract_terms; +/** + * Mock proposal data, not need to be well-formed + */ +static json_t *contract_terms_future; + /** @@ -433,75 +438,85 @@ run (void *cls) json_string ("backenddb test B"))); contract = json_object (); contract_terms = json_object (); + GNUNET_assert (0 == + json_object_set_new (contract_terms, + "order", + json_string ("1"))); + + contract_terms_future = json_object (); + GNUNET_assert (0 == + json_object_set_new (contract_terms_future, + "order", + json_string ("2"))); TALER_JSON_hash (contract_terms, &h_contract_terms); FAILIF (GNUNET_OK != plugin->insert_contract_terms (plugin->cls, - order_id, - &merchant_pub, - timestamp, - contract_terms)); + order_id, + &merchant_pub, + timestamp, + contract_terms)); json_t *out; FAILIF (GNUNET_OK != plugin->find_contract_terms (plugin->cls, - &out, - order_id, - &merchant_pub)); + &out, + order_id, + &merchant_pub)); FAILIF (GNUNET_OK != plugin->find_contract_terms_history (plugin->cls, - order_id, - &merchant_pub, - pd_cb, - NULL)); + order_id, + &merchant_pub, + &pd_cb, + NULL)); FAILIF (GNUNET_OK != plugin->find_contract_terms_from_hash (plugin->cls, - &out, - &h_contract_terms, - &merchant_pub)); + &out, + &h_contract_terms, + &merchant_pub)); FAILIF (1 != plugin->find_contract_terms_by_date_and_range (plugin->cls, - fake_now, - &merchant_pub, - 2, - 1, - GNUNET_NO, - pd_cb, - NULL)); + fake_now, + &merchant_pub, + 2, + 1, + GNUNET_NO, + &pd_cb, + NULL)); timestamp = GNUNET_TIME_absolute_get (); GNUNET_TIME_round_abs (×tamp); FAILIF (GNUNET_OK != plugin->insert_contract_terms (plugin->cls, - order_id_future, - &merchant_pub, - timestamp, - contract_terms)); + order_id_future, + &merchant_pub, + timestamp, + contract_terms_future)); fake_now = GNUNET_TIME_absolute_subtract (timestamp, delta); FAILIF (2 != plugin->find_contract_terms_by_date_and_range (plugin->cls, - fake_now, - &merchant_pub, - 0, - 5, - GNUNET_YES, - pd_cb, - NULL)); + fake_now, + &merchant_pub, + 0, + 5, + GNUNET_YES, + &pd_cb, + NULL)); FAILIF (0 != plugin->find_contract_terms_by_date (plugin->cls, - fake_now, - &merchant_pub, - 1, - pd_cb, - NULL)); + fake_now, + &merchant_pub, + 1, + &pd_cb, + NULL)); FAILIF (GNUNET_OK != plugin->store_transaction (plugin->cls, diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 2cef7945..6f926b14 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -23,6 +23,7 @@ #define TALER_MERCHANTDB_PLUGIN_H #include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> #include <jansson.h> /** @@ -191,9 +192,9 @@ struct TALER_MERCHANTDB_Plugin * @param merchant_pub merchant's public key * @param timestamp timestamp of this proposal data * @param contract_terms proposal data to store - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*insert_contract_terms) (void *cls, const char *order_id, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -207,10 +208,9 @@ struct TALER_MERCHANTDB_Plugin * @param[out] contract_terms where to store the result * @param order_id order_id used to lookup. * @param merchant_pub instance's public key. - * @return #GNUNET_OK on success, #GNUNET_NO if no contract is - * found, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_contract_terms) (void *cls, json_t **contract_terms, const char *order_id, @@ -224,10 +224,9 @@ struct TALER_MERCHANTDB_Plugin * @param[out] contract_terms where to store the result * @param h_contract_terms hashcode used to lookup. * @param merchant_pub instance's public key. - * @return #GNUNET_OK on success, #GNUNET_NO if no contract is - * found, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_contract_terms_from_hash) (void *cls, json_t **contract_terms, const struct GNUNET_HashCode *h_contract_terms, @@ -251,9 +250,9 @@ struct TALER_MERCHANTDB_Plugin * This is typically used to show live updates on the merchant's backoffice * @param cb function to call with transaction data, can be NULL. * @param cb_cls closure for @a cb - * @return numer of found tuples, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_contract_terms_by_date_and_range) (void *cls, struct GNUNET_TIME_Absolute date, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -272,10 +271,9 @@ struct TALER_MERCHANTDB_Plugin * @param merchant_pub public key of the merchant using this method * @param cb the callback * @param cb_cls closure to pass to @a cb - * @return #GNUNET_YES, #GNUNET_NO, #GNUNET_SYSERR according to the - * query being successful, unsuccessful, or generated errors. + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_contract_terms_history) (void *cls, const char *order_id, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -294,9 +292,9 @@ struct TALER_MERCHANTDB_Plugin * @param nrows only nrows rows are returned. * @param cb function to call with transaction data, can be NULL. * @param cb_cls closure for @a cb - * @return numer of found tuples, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_contract_terms_by_date) (void *cls, struct GNUNET_TIME_Absolute date, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -316,9 +314,9 @@ struct TALER_MERCHANTDB_Plugin * @param timestamp time of the confirmation * @param refund refund deadline * @param total_amount total amount we receive for the contract after fees - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*store_transaction) (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -340,9 +338,9 @@ struct TALER_MERCHANTDB_Plugin * @param deposit_fee fee the exchange will charge for this coin * @param signkey_pub public key used by the exchange for @a exchange_proof * @param exchange_proof proof from exchange that coin was accepted - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*store_deposit) (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -363,9 +361,9 @@ struct TALER_MERCHANTDB_Plugin * @param coin_pub public key of the coin * @param wtid identifier of the wire transfer in which the exchange * send us the money for the coin deposit - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*store_coin_to_transfer) (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -381,9 +379,9 @@ struct TALER_MERCHANTDB_Plugin * @param execution_time when was @a wtid executed * @param signkey_pub public key used by the exchange for @a exchange_proof * @param exchange_proof proof from exchange about what the deposit was for - * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*store_transfer_to_proof) (void *cls, const char *exchange_uri, const struct TALER_WireTransferIdentifierRawP *wtid, @@ -399,10 +397,9 @@ struct TALER_MERCHANTDB_Plugin * @param date limit to transactions' age * @param cb function to call with transaction data * @param cb_cls closure for @a cb - * @return #GNUNET_OK if found, #GNUNET_NO if not, #GNUNET_SYSERR - * upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_transactions_by_date) (void *cls, struct GNUNET_TIME_Absolute date, TALER_MERCHANTDB_TransactionCallback cb, @@ -416,9 +413,9 @@ struct TALER_MERCHANTDB_Plugin * @param merchant_pub merchant's public key. * @param cb function to call with transaction data * @param cb_cls closure for @a cb - * @return number of found tuples, #GNUNET_SYSERR upon error + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_transaction) (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -435,15 +432,15 @@ struct TALER_MERCHANTDB_Plugin * in order to find the result. * @param cb function to call with payment data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if h_contract_terms is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_payments) (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, TALER_MERCHANTDB_CoinDepositCallback cb, void *cb_cls); + /** * Lookup information about coin payments by h_contract_terms and coin. @@ -455,10 +452,9 @@ struct TALER_MERCHANTDB_Plugin * @param coin_pub public key to use for the search * @param cb function to call with payment data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if h_contract_terms is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_payments_by_hash_and_coin) (void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -478,10 +474,9 @@ struct TALER_MERCHANTDB_Plugin * @param h_contract_terms proposal data's hashcode * @param cb function to call with transfer data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if h_contract_terms is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_transfers_by_hash) (void *cls, const struct GNUNET_HashCode *h_contract_terms, TALER_MERCHANTDB_TransferCallback cb, @@ -495,10 +490,9 @@ struct TALER_MERCHANTDB_Plugin * @param wtid wire transfer identifier to find matching transactions for * @param cb function to call with payment data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if h_contract_terms is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_deposits_by_wtid) (void *cls, const struct TALER_WireTransferIdentifierRawP *wtid, TALER_MERCHANTDB_CoinDepositCallback cb, @@ -513,10 +507,9 @@ struct TALER_MERCHANTDB_Plugin * @param wtid wire transfer identifier for the search * @param cb function to call with proof data * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_NO if h_contract_terms is unknown, - * #GNUNET_SYSERR on hard errors + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*find_proof_by_wtid) (void *cls, const char *exchange_uri, const struct TALER_WireTransferIdentifierRawP *wtid, @@ -535,12 +528,9 @@ struct TALER_MERCHANTDB_Plugin * @param refund maximum refund to return to the customer for this contract * @param reason 0-terminated UTF-8 string giving the reason why the customer * got a refund (free form, business-specific) - * @return #GNUNET_OK if the refund is accepted - * #GNUNET_NO if the refund is at or below the previous refund amount - * #GNUNET_SYSERR on database error, i.e. contract unknown, DB on fire, - * (FIXME: distinguish hard/soft? who does retries?) + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*increase_refund_for_contract)(void *cls, const struct GNUNET_HashCode *h_contract_terms, const struct TALER_MerchantPublicKeyP *merchant_pub, @@ -556,11 +546,9 @@ struct TALER_MERCHANTDB_Plugin * @param h_contract_terms hash code of the contract * @param rc function to call for each coin on which there is a refund * @param rc_cls closure for @a rc - * @return #GNUNET_OK if we called @a rc on all coins - * #GNUNET_NO if there are no refunds for @a h_contract_terms - * #GNUNET_SYSERR if there were errors talking to the DB + * @return transaction status */ - int + enum GNUNET_DB_QueryStatus (*get_refunds_from_contract_terms_hash)(void *cls, const struct TALER_MerchantPublicKeyP *merchant_pub, const struct GNUNET_HashCode *h_contract_terms, |