/*
This file is part of TALER
(C) 2014--2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see
*/
/**
* @file plugin_merchantdb_postgres.c
* @brief database helper functions for postgres used by the merchant
* @author Sree Harsha Totakura
* @author Christian Grothoff
* @author Marcello Stanisci
* @author Priscilla Huang
* @author Iván Ávalos
*/
#include "platform.h"
#include
#include
#include
#include
#include
#include
#include "taler_merchantdb_plugin.h"
#include "pg_helper.h"
#include "pg_insert_account.h"
#include "pg_update_account.h"
#include "pg_lookup_instances.h"
#include "pg_lookup_transfers.h"
#include "pg_update_wirewatch_progress.h"
#include "pg_select_wirewatch_accounts.h"
#include "pg_select_open_transfers.h"
#include "pg_delete_exchange_accounts.h"
#include "pg_select_accounts_by_exchange.h"
#include "pg_insert_exchange_account.h"
#include "pg_lookup_reserves.h"
#include "pg_lookup_instance_auth.h"
#include "pg_update_transfer_status.h"
#include "pg_insert_instance.h"
#include "pg_account_kyc_set_status.h"
#include "pg_account_kyc_get_status.h"
#include "pg_delete_instance_private_key.h"
#include "pg_purge_instance.h"
#include "pg_update_instance.h"
#include "pg_update_instance_auth.h"
#include "pg_inactivate_account.h"
#include "pg_activate_account.h"
#include "pg_lookup_products.h"
#include "pg_lookup_product.h"
#include "pg_delete_product.h"
#include "pg_insert_product.h"
#include "pg_update_product.h"
#include "pg_lock_product.h"
#include "pg_expire_locks.h"
#include "pg_delete_order.h"
#include "pg_lookup_order.h"
#include "pg_lookup_order_summary.h"
#include "pg_lookup_orders.h"
#include "pg_insert_order.h"
#include "pg_unlock_inventory.h"
#include "pg_insert_order_lock.h"
#include "pg_lookup_contract_terms2.h"
#include "pg_lookup_contract_terms.h"
#include "pg_insert_contract_terms.h"
#include "pg_update_contract_terms.h"
#include "pg_delete_contract_terms.h"
#include "pg_lookup_deposits.h"
#include "pg_insert_exchange_signkey.h"
#include "pg_insert_deposit.h"
#include "pg_lookup_refunds.h"
#include "pg_mark_contract_paid.h"
#include "pg_refund_coin.h"
#include "pg_lookup_order_status.h"
#include "pg_lookup_order_status_by_serial.h"
#include "pg_lookup_payment_status.h"
#include "pg_set_transfer_status_to_confirmed.h"
#include "pg_insert_exchange_keys.h"
#include "pg_select_exchange_keys.h"
#include "pg_insert_deposit_to_transfer.h"
#include "pg_increase_refund.h"
#include "pg_insert_transfer.h"
#include "pg_insert_transfer_details.h"
#include "pg_store_wire_fee_by_exchange.h"
#include "pg_insert_reserve.h"
#include "pg_activate_reserve.h"
#include "pg_authorize_reward.h"
#include "pg_insert_pickup.h"
/**
* How often do we re-try if we run into a DB serialization error?
*/
#define MAX_RETRIES 3
/**
* Drop all Taler tables. This should only be used by testcases.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
*/
static enum GNUNET_GenericReturnValue
postgres_drop_tables (void *cls)
{
struct PostgresClosure *pc = cls;
struct GNUNET_PQ_Context *conn;
enum GNUNET_GenericReturnValue ret;
conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
"merchantdb-postgres",
NULL,
NULL,
NULL);
if (NULL == conn)
return GNUNET_SYSERR;
ret = GNUNET_PQ_exec_sql (conn,
"drop");
GNUNET_PQ_disconnect (conn);
return ret;
}
/**
* Initialize tables.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
*/
static enum GNUNET_GenericReturnValue
postgres_create_tables (void *cls)
{
struct PostgresClosure *pc = cls;
struct GNUNET_PQ_Context *conn;
struct GNUNET_PQ_ExecuteStatement es[] = {
GNUNET_PQ_make_try_execute ("SET search_path TO merchant;"),
GNUNET_PQ_EXECUTE_STATEMENT_END
};
conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
"merchantdb-postgres",
"merchant-",
es,
NULL);
if (NULL == conn)
return GNUNET_SYSERR;
GNUNET_PQ_disconnect (conn);
return GNUNET_OK;
}
/**
* Register callback to be invoked on events of type @a es.
*
* @param cls database context to use
* @param es specification of the event to listen for
* @param timeout how long to wait for the event
* @param cb function to call when the event happens, possibly
* mulrewardle times (until cancel is invoked)
* @param cb_cls closure for @a cb
* @return handle useful to cancel the listener
*/
static struct GNUNET_DB_EventHandler *
postgres_event_listen (void *cls,
const struct GNUNET_DB_EventHeaderP *es,
struct GNUNET_TIME_Relative timeout,
GNUNET_DB_EventCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
return GNUNET_PQ_event_listen (pg->conn,
es,
timeout,
cb,
cb_cls);
}
/**
* Stop notifications.
*
* @param eh handle to unregister.
*/
static void
postgres_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
{
GNUNET_PQ_event_listen_cancel (eh);
}
/**
* Notify all that listen on @a es of an event.
*
* @param cls database context to use
* @param es specification of the event to generate
* @param extra additional event data provided
* @param extra_size number of bytes in @a extra
*/
static void
postgres_event_notify (void *cls,
const struct GNUNET_DB_EventHeaderP *es,
const void *extra,
size_t extra_size)
{
struct PostgresClosure *pg = cls;
return GNUNET_PQ_event_notify (pg->conn,
es,
extra,
extra_size);
}
void
postgres_preflight (void *cls)
{
struct PostgresClosure *pg = cls;
if (NULL == pg->transaction_name)
return; /* all good */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"BUG: Preflight check detected running transaction `%s'!\n",
pg->transaction_name);
GNUNET_assert (0);
}
void
check_connection (struct PostgresClosure *pg)
{
if (NULL != pg->transaction_name)
return;
GNUNET_PQ_reconnect_if_down (pg->conn);
}
/**
* Closure for lookup_deposits_by_order_cb().
*/
struct LookupDepositsByOrderContext
{
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Function to call with all results.
*/
TALER_MERCHANTDB_DepositedCoinsCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Set to the query result.
*/
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 LookupDepositsByOrderContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_deposits_by_order_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupDepositsByOrderContext *ldoc = cls;
struct PostgresClosure *pg = ldoc->pg;
for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
ldoc->cb (ldoc->cb_cls,
deposit_serial,
exchange_url,
&h_wire,
&amount_with_fee,
&deposit_fee,
&coin_pub);
GNUNET_PQ_cleanup_result (rs); /* technically useless here */
}
ldoc->qs = num_results;
}
/**
* Retrieve details about coins that were deposited for an order.
*
* @param cls closure
* @param order_serial identifies the order
* @param cb function to call for each deposited coin
* @param cb_cls closure for @a cb
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_deposits_by_order (void *cls,
uint64_t order_serial,
TALER_MERCHANTDB_DepositedCoinsCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupDepositsByOrderContext ldoc = {
.pg = pg,
.cb = cb,
.cb_cls = cb_cls
};
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&order_serial),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_deposits_by_order",
params,
&lookup_deposits_by_order_cb,
&ldoc);
if (qs < 0)
return qs;
return ldoc.qs;
}
/**
* Closure for lookup_deposits_by_order_cb().
*/
struct LookupTransferDetailsByOrderContext
{
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Function to call with all results.
*/
TALER_MERCHANTDB_OrderTransferDetailsCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Set to the query result.
*/
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 LookupTransferDetailsByOrderContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_transfer_details_by_order_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupTransferDetailsByOrderContext *ltdo = cls;
struct PostgresClosure *pg = ltdo->pg;
for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
ltdo->cb (ltdo->cb_cls,
&wtid,
exchange_url,
execution_time,
&deposit_value,
&deposit_fee,
(0 != transfer_confirmed));
GNUNET_PQ_cleanup_result (rs); /* technically useless here */
}
ltdo->qs = num_results;
}
/**
* Retrieve wire transfer details for all deposits associated with
* a given @a order_serial.
*
* @param cls closure
* @param order_serial identifies the order
* @param cb function called with the wire transfer details
* @param cb_cls closure for @a cb
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_transfer_details_by_order (
void *cls,
uint64_t order_serial,
TALER_MERCHANTDB_OrderTransferDetailsCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupTransferDetailsByOrderContext ltdo = {
.pg = pg,
.cb = cb,
.cb_cls = cb_cls
};
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&order_serial),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_multi_select (
pg->conn,
"lookup_transfer_details_by_order",
params,
&lookup_transfer_details_by_order_cb,
<do);
if (qs < 0)
return qs;
return ltdo.qs;
}
/**
* Set 'wired' status for an order to 'true'.
*
* @param cls closure
* @param order_serial serial number of the order
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_mark_order_wired (void *cls,
uint64_t order_serial)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&order_serial),
GNUNET_PQ_query_param_end
};
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"mark_order_wired",
params);
}
/**
* Closure for #lookup_refunds_detailed_cb().
*/
struct LookupRefundsDetailedContext
{
/**
* Function to call for each refund.
*/
TALER_MERCHANTDB_RefundDetailCallback rc;
/**
* Closure for @e rc.
*/
void *rc_cls;
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Transaction result.
*/
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 GetRefundsContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_refunds_detailed_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupRefundsDetailedContext *lrdc = cls;
struct PostgresClosure *pg = lrdc->pg;
for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
lrdc->rc (lrdc->rc_cls,
refund_serial,
timestamp,
&coin_pub,
exchange_url,
rtransaction_id,
reason,
&refund_amount,
0 != pending8);
GNUNET_PQ_cleanup_result (rs);
}
lrdc->qs = num_results;
}
/**
* Obtain detailed refund data associated with a contract.
*
* @param cls closure, typically a connection to the db
* @param instance_id instance to lookup refunds for
* @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_lookup_refunds_detailed (
void *cls,
const char *instance_id,
const struct TALER_PrivateContractHashP *h_contract_terms,
TALER_MERCHANTDB_RefundDetailCallback rc,
void *rc_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
GNUNET_PQ_query_param_end
};
struct LookupRefundsDetailedContext lrdc = {
.rc = rc,
.rc_cls = rc_cls,
.pg = pg
};
enum GNUNET_DB_QueryStatus qs;
/* no preflight check here, run in transaction by caller! */
TALER_LOG_DEBUG ("Looking for refund %s + %s\n",
GNUNET_h2s (&h_contract_terms->hash),
instance_id);
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_refunds_detailed",
params,
&lookup_refunds_detailed_cb,
&lrdc);
if (0 >= qs)
return qs;
return lrdc.qs;
}
/**
* Insert refund proof data from the exchange into the database.
*
* @param cls closure
* @param refund_serial serial number of the refund
* @param exchange_sig signature from exchange that coin was refunded
* @param exchange_pub signing key that was used for @a exchange_sig
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_insert_refund_proof (
void *cls,
uint64_t refund_serial,
const struct TALER_ExchangeSignatureP *exchange_sig,
const struct TALER_ExchangePublicKeyP *exchange_pub)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&refund_serial),
GNUNET_PQ_query_param_auto_from_type (exchange_sig),
GNUNET_PQ_query_param_auto_from_type (exchange_pub),
GNUNET_PQ_query_param_end
};
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_refund_proof",
params);
}
/**
* Lookup refund proof data.
*
* @param cls closure
* @param refund_serial serial number of the refund
* @param[out] exchange_sig set to signature from exchange
* @param[out] exchange_pub signing key that was used for @a exchange_sig
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_refund_proof (void *cls,
uint64_t refund_serial,
struct TALER_ExchangeSignatureP *exchange_sig,
struct TALER_ExchangePublicKeyP *exchange_pub)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&refund_serial),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
exchange_sig),
GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
exchange_pub),
GNUNET_PQ_result_spec_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_refund_proof",
params,
rs);
}
/**
* Retrieve the order ID that was used to pay for a resource within a session.
*
* @param cls closure
* @param instance_id identifying the instance
* @param fulfillment_url URL that canonically identifies the resource
* being paid for
* @param session_id session id
* @param[out] order_id where to store the order ID that was used when
* paying for the resource URL
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
postgres_lookup_order_by_fulfillment (void *cls,
const char *instance_id,
const char *fulfillment_url,
const char *session_id,
char **order_id)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (fulfillment_url),
GNUNET_PQ_query_param_string (session_id),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("order_id",
order_id),
GNUNET_PQ_result_spec_end
};
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_order_by_fulfillment",
params,
rs);
}
/**
* Delete information about a transfer. Note that transfers
* confirmed by the exchange cannot be deleted anymore.
*
* @param cls closure
* @param instance_id instance to delete transfer of
* @param transfer_serial_id transfer to delete
* @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
* if deletion is prohibited OR transfer is unknown
*/
static enum GNUNET_DB_QueryStatus
postgres_delete_transfer (void *cls,
const char *instance_id,
uint64_t transfer_serial_id)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_uint64 (&transfer_serial_id),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"delete_transfer",
params);
}
/**
* Check if information about a transfer exists with the
* backend. Returns no data, only the query status.
*
* @param cls closure
* @param instance_id instance to delete transfer of
* @param transfer_serial_id transfer to delete
* @return DB status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
* if the transfer record exists
*/
static enum GNUNET_DB_QueryStatus
postgres_check_transfer_exists (void *cls,
const char *instance_id,
uint64_t transfer_serial_id)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_uint64 (&transfer_serial_id),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"check_transfer_exists",
params,
rs);
}
/**
* Lookup account serial by payto URI.
*
* @param cls closure
* @param instance_id instance to lookup the account from
* @param payto_uri what is the merchant's bank account to lookup
* @param[out] account_serial serial number of the account
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_account (void *cls,
const char *instance_id,
const char *payto_uri,
uint64_t *account_serial)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (payto_uri),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("account_serial",
account_serial),
GNUNET_PQ_result_spec_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_account",
params,
rs);
}
/**
* Obtain information about wire fees charged by an exchange,
* including signature (so we have proof).
*
* @param cls closure
* @param master_pub public key of the exchange
* @param wire_method the wire method
* @param contract_date date of the contract to use for the lookup
* @param[out] fees wire fees charged
* @param[out] start_date start of fee being used
* @param[out] end_date end of fee being used
* @param[out] master_sig signature of exchange over fee structure
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_wire_fee (void *cls,
const struct TALER_MasterPublicKeyP *master_pub,
const char *wire_method,
struct GNUNET_TIME_Timestamp contract_date,
struct TALER_WireFeeSet *fees,
struct GNUNET_TIME_Timestamp *start_date,
struct GNUNET_TIME_Timestamp *end_date,
struct TALER_MasterSignatureP *master_sig)
{
struct PostgresClosure *pg = cls;
struct GNUNET_HashCode h_wire_method;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_auto_from_type (&h_wire_method),
GNUNET_PQ_query_param_timestamp (&contract_date),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
&fees->wire),
TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
&fees->closing),
GNUNET_PQ_result_spec_timestamp ("start_date",
start_date),
GNUNET_PQ_result_spec_timestamp ("end_date",
end_date),
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
master_sig),
GNUNET_PQ_result_spec_end
};
check_connection (pg);
GNUNET_CRYPTO_hash (wire_method,
strlen (wire_method) + 1,
&h_wire_method);
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_wire_fee",
params,
rs);
}
/**
* Closure for #lookup_deposits_by_contract_and_coin_cb().
*/
struct LookupDepositsByCnCContext
{
/**
* Function to call for each deposit.
*/
TALER_MERCHANTDB_CoinDepositCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Transaction result.
*/
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 LookupDepositsByCnCContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_deposits_by_contract_and_coin_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupDepositsByCnCContext *ldcc = cls;
struct PostgresClosure *pg = ldcc->pg;
for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
ldcc->cb (ldcc->cb_cls,
exchange_url,
&amount_with_fee,
&deposit_fee,
&refund_fee,
&wire_fee,
&h_wire,
deposit_timestamp,
refund_deadline,
&exchange_sig,
&exchange_pub);
GNUNET_PQ_cleanup_result (rs);
}
ldcc->qs = num_results;
}
/**
* Lookup information about coin payments by @a h_contract_terms and
* @a coin_pub.
*
* @param cls closure
* @param instance_id instance to lookup payments for
* @param h_contract_terms proposal data's hashcode
* @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 transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_deposits_by_contract_and_coin (
void *cls,
const char *instance_id,
const struct TALER_PrivateContractHashP *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
TALER_MERCHANTDB_CoinDepositCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_end
};
struct LookupDepositsByCnCContext ldcc = {
.cb = cb,
.cb_cls = cb_cls,
.pg = pg
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (
pg->conn,
"lookup_deposits_by_contract_and_coin",
params,
&lookup_deposits_by_contract_and_coin_cb,
&ldcc);
if (0 >= qs)
return qs;
return ldcc.qs;
}
/**
* Lookup transfer status.
*
* @param cls closure
* @param instance_id at which instance should we resolve the transfer
* @param exchange_url the exchange that made the transfer
* @param wtid wire transfer subject
* @param[out] total_amount amount that was debited from our
* aggregate balance at the exchange (in total, sum of
* the wire transfer amount and the @a wire_fee)
* @param[out] wire_fee the wire fee the exchange charged (only set if @a have_exchange_sig is true)
* @param[out] exchange_amount the amount the exchange claims was transferred (only set if @a have_exchange_sig is true)
* @param[out] execution_time when the transfer was executed by the exchange (only set if @a have_exchange_sig is true)
* @param[out] have_exchange_sig do we have a response from the exchange about this transfer
* @param[out] verified did we confirm the transfer was OK
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_transfer (
void *cls,
const char *instance_id,
const char *exchange_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct TALER_Amount *total_amount,
struct TALER_Amount *wire_fee,
struct TALER_Amount *exchange_amount,
struct GNUNET_TIME_Timestamp *execution_time,
bool *have_exchange_sig,
bool *verified)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (exchange_url),
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_end
};
uint8_t verified8;
/** Amount we got actually credited, _excludes_ the wire fee */
bool no_sig;
struct TALER_Amount credit_amount;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount",
&credit_amount),
GNUNET_PQ_result_spec_allow_null (
TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
wire_fee),
&no_sig),
GNUNET_PQ_result_spec_allow_null (
TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_amount",
exchange_amount),
NULL),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_timestamp ("execution_time",
execution_time),
NULL),
GNUNET_PQ_result_spec_auto_from_type ("verified",
&verified8),
GNUNET_PQ_result_spec_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
*execution_time = GNUNET_TIME_UNIT_ZERO_TS;
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_transfer",
params,
rs);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Lookup transfer returned %d\n",
qs);
if (qs > 0)
{
*have_exchange_sig = ! no_sig;
*verified = (0 != verified8);
if ( (! no_sig) &&
(0 >
TALER_amount_add (total_amount,
&credit_amount,
wire_fee)) )
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
else
{
*verified = false;
*have_exchange_sig = false;
}
return qs;
}
/**
* Closure for #lookup_transfer_summary_cb().
*/
struct LookupTransferSummaryContext
{
/**
* Function to call for each order that was aggregated.
*/
TALER_MERCHANTDB_TransferSummaryCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Transaction result.
*/
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 LookupTransferSummaryContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_transfer_summary_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupTransferSummaryContext *ltdc = cls;
struct PostgresClosure *pg = ltdc->pg;
for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
ltdc->cb (ltdc->cb_cls,
order_id,
&deposit_value,
&deposit_fee);
GNUNET_PQ_cleanup_result (rs);
}
ltdc->qs = num_results;
}
/**
* Lookup transfer summary.
*
* @param cls closure
* @param exchange_url the exchange that made the transfer
* @param wtid wire transfer subject
* @param cb function to call with detailed transfer data
* @param cb_cls closure for @a cb
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_transfer_summary (
void *cls,
const char *exchange_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MERCHANTDB_TransferSummaryCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (exchange_url),
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_end
};
struct LookupTransferSummaryContext ltdc = {
.cb = cb,
.cb_cls = cb_cls,
.pg = pg
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (
pg->conn,
"lookup_transfer_summary",
params,
&lookup_transfer_summary_cb,
<dc);
if (0 >= qs)
return qs;
return ltdc.qs;
}
/**
* Closure for #lookup_transfer_details_cb().
*/
struct LookupTransferDetailsContext
{
/**
* Function to call for each order that was aggregated.
*/
TALER_MERCHANTDB_TransferDetailsCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Plugin context.
*/
struct PostgresClosure *pg;
/**
* Transaction result.
*/
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 LookupTransferDetailsContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_transfer_details_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupTransferDetailsContext *ltdc = cls;
struct PostgresClosure *pg = ltdc->pg;
for (unsigned int i = 0; iqs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
ltdc->cb (ltdc->cb_cls,
(unsigned int) current_offset,
&ttd);
GNUNET_PQ_cleanup_result (rs);
}
ltdc->qs = num_results;
}
/**
* Lookup transfer details.
*
* @param cls closure
* @param exchange_url the exchange that made the transfer
* @param wtid wire transfer subject
* @param cb function to call with detailed transfer data
* @param cb_cls closure for @a cb
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_transfer_details (
void *cls,
const char *exchange_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MERCHANTDB_TransferDetailsCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (exchange_url),
GNUNET_PQ_query_param_auto_from_type (wtid),
GNUNET_PQ_query_param_end
};
struct LookupTransferDetailsContext ltdc = {
.cb = cb,
.cb_cls = cb_cls,
.pg = pg
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (
pg->conn,
"lookup_transfer_details",
params,
&lookup_transfer_details_cb,
<dc);
if (0 >= qs)
return qs;
return ltdc.qs;
}
/**
* Closure for #lookup_pending_reserves_cb.
*/
struct LookupPendingReservesContext
{
/**
* Postgres context.
*/
struct PostgresClosure *pg;
/**
* Function to call with the results
*/
TALER_MERCHANTDB_PendingReservesCallback cb;
/**
* Closure for @e cb
*/
void *cb_cls;
/**
* Set in case of errors.
*/
enum GNUNET_DB_QueryStatus qs;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about accounts.
*
* @param[in,out] cls of type `struct LookupReservesContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_pending_reserves_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupPendingReservesContext *lrc = cls;
struct PostgresClosure *pg = lrc->pg;
for (unsigned int i = 0; i < num_results; i++)
{
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_Amount merchant_initial_balance;
char *exchange_url;
char *instance_id;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub),
GNUNET_PQ_result_spec_string ("merchant_id",
&instance_id),
GNUNET_PQ_result_spec_string ("exchange_url",
&exchange_url),
TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
&merchant_initial_balance),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
lrc->cb (lrc->cb_cls,
instance_id,
exchange_url,
&reserve_pub,
&merchant_initial_balance);
GNUNET_PQ_cleanup_result (rs);
}
}
/**
* Lookup reserves pending activation across all instances.
*
* @param cls closure
* @param cb function to call with reserve summary data
* @param cb_cls closure for @a cb
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_pending_reserves (void *cls,
TALER_MERCHANTDB_PendingReservesCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupPendingReservesContext lrc = {
.pg = pg,
.cb = cb,
.cb_cls = cb_cls
};
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_pending_reserves",
params,
&lookup_pending_reserves_cb,
&lrc);
if (lrc.qs < 0)
return lrc.qs;
return qs;
}
/**
* Closure for #lookup_reserve_rewards_cb().
*/
struct LookupRewardsContext
{
/**
* Postgres context.
*/
struct PostgresClosure *pg;
/**
* Array with information about rewards generated from this reserve.
*/
struct TALER_MERCHANTDB_RewardDetails *rewards;
/**
* Length of the @e rewards array.
*/
unsigned int rewards_length;
/**
* Set in case of errors.
*/
enum GNUNET_DB_QueryStatus qs;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about accounts.
*
* @param[in,out] cls of type `struct LookupRewardsContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_reserve_rewards_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupRewardsContext *ltc = cls;
struct PostgresClosure *pg = ltc->pg;
GNUNET_array_grow (ltc->rewards,
ltc->rewards_length,
num_results);
for (unsigned int i = 0; i < num_results; i++)
{
struct TALER_MERCHANTDB_RewardDetails *td = <c->rewards[i];
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("justification",
&td->reason),
GNUNET_PQ_result_spec_auto_from_type ("reward_id",
&td->reward_id),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
&td->total_amount),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
ltc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
}
}
/**
* Lookup reserve details.
*
* @param cls closure
* @param instance_id instance to lookup payments for
* @param reserve_pub public key of the reserve to inspect
* @param fetch_rewards if true, also return information about rewards
* @param cb function to call with reserve summary data
* @param cb_cls closure for @a cb
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_reserve (void *cls,
const char *instance_id,
const struct TALER_ReservePublicKeyP *reserve_pub,
bool fetch_rewards,
TALER_MERCHANTDB_ReserveDetailsCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupRewardsContext ltc = {
.pg = pg,
.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
};
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_end
};
struct GNUNET_TIME_Timestamp creation_time;
struct GNUNET_TIME_Timestamp expiration_time;
struct TALER_Amount merchant_initial_balance;
struct TALER_Amount exchange_initial_balance;
struct TALER_Amount pickup_amount;
struct TALER_Amount committed_amount;
struct TALER_MasterPublicKeyP master_pub;
bool active;
char *exchange_url = NULL;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_timestamp ("creation_time",
&creation_time),
GNUNET_PQ_result_spec_timestamp ("expiration",
&expiration_time),
TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
&merchant_initial_balance),
TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
&exchange_initial_balance),
TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_picked_up",
&pickup_amount),
TALER_PQ_RESULT_SPEC_AMOUNT ("rewards_committed",
&committed_amount),
GNUNET_PQ_result_spec_auto_from_type ("master_pub",
&master_pub),
GNUNET_PQ_result_spec_bool ("active",
&active),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("exchange_url",
&exchange_url),
NULL),
GNUNET_PQ_result_spec_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_reserve",
params,
rs);
if (qs < 0)
return qs;
if (! fetch_rewards)
{
cb (cb_cls,
creation_time,
expiration_time,
&merchant_initial_balance,
&exchange_initial_balance,
&pickup_amount,
&committed_amount,
active,
&master_pub,
exchange_url,
0,
NULL);
GNUNET_PQ_cleanup_result (rs);
return qs;
}
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_reserve_rewards",
params,
&lookup_reserve_rewards_cb,
<c);
if (qs < 0)
return qs;
if (ltc.qs >= 0)
{
cb (cb_cls,
creation_time,
expiration_time,
&merchant_initial_balance,
&exchange_initial_balance,
&pickup_amount,
&committed_amount,
active,
&master_pub,
exchange_url,
ltc.rewards_length,
ltc.rewards);
}
for (unsigned int i = 0; iconn,
"delete_reserve",
params);
}
/**
* Purge all of the information about a reserve, including rewards.
*
* @param cls closure, typically a connection to the db
* @param instance_id which instance is the reserve tied to
* @param reserve_pub which reserve is to be purged
* @return transaction status, usually
* #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
*/
static enum GNUNET_DB_QueryStatus
postgres_purge_reserve (void *cls,
const char *instance_id,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"purge_reserve",
params);
}
/**
* Closure for #lookup_signatures_cb().
*/
struct LookupSignaturesContext
{
/**
* Length of the @e sigs array
*/
unsigned int sigs_length;
/**
* Where to store the signatures.
*/
struct TALER_BlindedDenominationSignature *sigs;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about accounts.
*
* @param[in,out] cls of type `struct LookupSignaturesContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_signatures_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupSignaturesContext *lsc = cls;
for (unsigned int i = 0; i < num_results; i++)
{
uint32_t offset;
struct TALER_BlindedDenominationSignature bsig;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint32 ("coin_offset",
&offset),
TALER_PQ_result_spec_blinded_denom_sig ("blind_sig",
&bsig),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
return;
}
if (offset >= lsc->sigs_length)
{
GNUNET_break_op (0);
GNUNET_PQ_cleanup_result (rs);
continue;
}
/* Must be NULL due to UNIQUE constraint on offset and
requirement that client launched us with 'sigs'
pre-initialized to NULL. */
lsc->sigs[offset] = bsig;
}
}
/**
* Lookup pickup details for pickup @a pickup_id.
*
* @param cls closure, typically a connection to the db
* @param instance_id which instance should we lookup reward details for
* @param reward_id which reward should we lookup details on
* @param pickup_id which pickup should we lookup details on
* @param[out] exchange_url which exchange is the reward withdrawn from
* @param[out] reserve_priv private key the reward is withdrawn from (set if still available!)
* @param sigs_length length of the @a sigs array
* @param[out] sigs set to the (blind) signatures we have for this @a pickup_id,
* those that are unavailable are left at NULL
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_pickup (void *cls,
const char *instance_id,
const struct TALER_RewardIdentifierP *reward_id,
const struct TALER_PickupIdentifierP *pickup_id,
char **exchange_url,
struct TALER_ReservePrivateKeyP *reserve_priv,
unsigned int sigs_length,
struct TALER_BlindedDenominationSignature sigs[])
{
struct PostgresClosure *pg = cls;
uint64_t pickup_serial;
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (reward_id),
GNUNET_PQ_query_param_auto_from_type (pickup_id),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("exchange_url",
exchange_url),
GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
reserve_priv),
GNUNET_PQ_result_spec_uint64 ("pickup_serial",
&pickup_serial),
GNUNET_PQ_result_spec_end
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_pickup",
params,
rs);
if (qs <= 0)
return qs;
}
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&pickup_serial),
GNUNET_PQ_query_param_end
};
struct LookupSignaturesContext lsc = {
.sigs_length = sigs_length,
.sigs = sigs
};
return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_pickup_signatures",
params,
&lookup_signatures_cb,
&lsc);
}
}
/**
* Lookup reward details for reward @a reward_id.
*
* @param cls closure, typically a connection to the db
* @param instance_id which instance should we lookup reward details for
* @param reward_id which reward should we lookup details on
* @param[out] total_authorized amount how high is the reward (with fees)
* @param[out] total_picked_up how much of the reward was so far picked up (with fees)
* @param[out] expiration set to when the reward expires
* @param[out] exchange_url set to the exchange URL where the reserve is
* @param[out] next_url set to the URL where the wallet should navigate after getting the reward
* @param[out] reserve_priv set to private key of reserve to be debited
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_reward (void *cls,
const char *instance_id,
const struct TALER_RewardIdentifierP *reward_id,
struct TALER_Amount *total_authorized,
struct TALER_Amount *total_picked_up,
struct GNUNET_TIME_Timestamp *expiration,
char **exchange_url,
char **next_url,
struct TALER_ReservePrivateKeyP *reserve_priv)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (reward_id),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
total_authorized),
TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up",
total_picked_up),
GNUNET_PQ_result_spec_timestamp ("expiration",
expiration),
GNUNET_PQ_result_spec_string ("exchange_url",
exchange_url),
GNUNET_PQ_result_spec_string ("next_url",
next_url),
GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
reserve_priv),
GNUNET_PQ_result_spec_end
};
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_reward",
params,
rs);
}
/**
* Context used for postgres_lookup_rewards().
*/
struct LookupMerchantRewardsContext
{
/**
* Postgres context.
*/
struct PostgresClosure *pg;
/**
* Function to call with the results.
*/
TALER_MERCHANTDB_RewardsCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Internal result.
*/
enum GNUNET_DB_QueryStatus qs;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about rewards.
*
* @param[in,out] cls of type `struct LookupRewardsContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_rewards_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupMerchantRewardsContext *plc = cls;
struct PostgresClosure *pg = plc->pg;
for (unsigned int i = 0; i < num_results; i++)
{
uint64_t row_id;
struct TALER_RewardIdentifierP reward_id;
struct TALER_Amount reward_amount;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("reward_serial",
&row_id),
GNUNET_PQ_result_spec_auto_from_type ("reward_id",
&reward_id),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
&reward_amount),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
plc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return;
}
plc->cb (plc->cb_cls,
row_id,
reward_id,
reward_amount);
GNUNET_PQ_cleanup_result (rs);
}
}
/**
* Lookup rewards
*
* @param cls closure, typically a connection to the db
* @param instance_id which instance should we lookup rewards for
* @param expired should we include expired rewards?
* @param limit maximum number of results to return, positive for
* ascending row id, negative for descending
* @param offset row id to start returning results from
* @param cb function to call with reward data
* @param cb_cls closure for @a cb
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_rewards (void *cls,
const char *instance_id,
enum TALER_EXCHANGE_YesNoAll expired,
int64_t limit,
uint64_t offset,
TALER_MERCHANTDB_RewardsCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupMerchantRewardsContext plc = {
.pg = pg,
.cb = cb,
.cb_cls = cb_cls
};
uint64_t ulimit = (limit > 0) ? limit : -limit;
uint8_t bexpired;
struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_uint64 (&ulimit),
GNUNET_PQ_query_param_uint64 (&offset),
GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_auto_from_type (&bexpired),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
char stmt[128];
bexpired = (TALER_EXCHANGE_YNA_YES == expired);
GNUNET_snprintf (stmt,
sizeof (stmt),
"lookup_rewards_%s%s",
(limit > 0) ? "inc" : "dec",
(TALER_EXCHANGE_YNA_ALL == expired) ? "" : "_expired");
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
stmt,
params,
&lookup_rewards_cb,
&plc);
if (0 != plc.qs)
return plc.qs;
return qs;
}
/**
* Closure for #lookup_pickup_details_cb().
*/
struct LookupRewardDetailsContext
{
/**
* Length of the @e sigs array
*/
unsigned int *pickups_length;
/**
* Where to store the signatures.
*/
struct TALER_MERCHANTDB_PickupDetails **pickups;
/**
* Database handle.
*/
struct PostgresClosure *pg;
/**
* Transaction status.
*/
enum GNUNET_DB_QueryStatus qs;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about pickups.
*
* @param[in,out] cls of type `struct LookupRewardDetailsContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_pickup_details_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupRewardDetailsContext *ltdc = cls;
struct PostgresClosure *pg = ltdc->pg;
*ltdc->pickups_length = num_results;
*ltdc->pickups = GNUNET_new_array (num_results,
struct TALER_MERCHANTDB_PickupDetails);
for (unsigned int i = 0; i < num_results; i++)
{
struct TALER_MERCHANTDB_PickupDetails *pd = &((*ltdc->pickups)[i]);
uint64_t num_planchets = 0;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("pickup_id",
&pd->pickup_id),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
&pd->requested_amount),
GNUNET_PQ_result_spec_uint64 ("num_planchets",
&num_planchets),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
GNUNET_array_grow (*ltdc->pickups,
*ltdc->pickups_length,
0);
return;
}
pd->num_planchets = num_planchets;
}
}
/**
* Lookup reward details for reward @a reward_id.
*
* @param cls closure, typically a connection to the db
* @param instance_id which instance should we lookup reward details for
* @param reward_id which reward should we lookup details on
* @param fpu should we fetch details about individual pickups
* @param[out] total_authorized amount how high is the reward (with fees)
* @param[out] total_picked_up how much of the reward was so far picked up (with fees)
* @param[out] justification why was the reward approved
* @param[out] expiration set to when the reward expires
* @param[out] reserve_pub set to which reserve is debited
* @param[out] pickups_length set to the length of @e pickups
* @param[out] pickups if @a fpu is true, set to details about the pickup operations
* @return transaction status,
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_reward_details (void *cls,
const char *instance_id,
const struct TALER_RewardIdentifierP *reward_id,
bool fpu,
struct TALER_Amount *total_authorized,
struct TALER_Amount *total_picked_up,
char **justification,
struct GNUNET_TIME_Timestamp *expiration,
struct TALER_ReservePublicKeyP *reserve_pub,
unsigned int *pickups_length,
struct TALER_MERCHANTDB_PickupDetails **pickups)
{
struct PostgresClosure *pg = cls;
uint64_t reward_serial;
enum GNUNET_DB_QueryStatus qs;
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_auto_from_type (reward_id),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("reward_serial",
&reward_serial),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
total_authorized),
TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up",
total_picked_up),
GNUNET_PQ_result_spec_string ("justification",
justification),
GNUNET_PQ_result_spec_timestamp ("expiration",
expiration),
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
reserve_pub),
GNUNET_PQ_result_spec_end
};
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_reward_details",
params,
rs);
if (qs <= 0)
return qs;
if (! fpu)
{
*pickups_length = 0;
*pickups = NULL;
return qs;
}
}
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&reward_serial),
GNUNET_PQ_query_param_end
};
struct LookupRewardDetailsContext ltdc = {
.pickups_length = pickups_length,
.pickups = pickups,
.pg = pg,
.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
};
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_pickup_details",
params,
&lookup_pickup_details_cb,
<dc);
if (qs < 0)
return qs;
return ltdc.qs;
}
}
/**
* Insert blind signature obtained from the exchange during a
* reward pickup operation.
*
* @param cls closure, typically a connection to the db
* @param pickup_id unique ID for the operation
* @param offset offset of the blind signature for the pickup
* @param blind_sig the blind signature
* @return transaction status, usually
* #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
* #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
*/
static enum GNUNET_DB_QueryStatus
postgres_insert_pickup_blind_signature (
void *cls,
const struct TALER_PickupIdentifierP *pickup_id,
uint32_t offset,
const struct TALER_BlindedDenominationSignature *blind_sig)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (pickup_id),
GNUNET_PQ_query_param_uint32 (&offset),
TALER_PQ_query_param_blinded_denom_sig (blind_sig),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_pickup_blind_signature",
params);
}
/**
* Delete information about a template.
*
* @param cls closure
* @param instance_id instance to delete template of
* @param template_id template to delete
* @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
* if template unknown.
*/
static enum GNUNET_DB_QueryStatus
postgres_delete_template (void *cls,
const char *instance_id,
const char *template_id)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (template_id),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"delete_template",
params);
}
/**
* Insert details about a particular template.
*
* @param cls closure
* @param instance_id instance to insert template for
* @param template_id template identifier of template to insert
* @param td the template details to insert
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_insert_template (void *cls,
const char *instance_id,
const char *template_id,
const struct TALER_MERCHANTDB_TemplateDetails *td)
{
struct PostgresClosure *pg = cls;
uint32_t pos32 = (uint32_t) td->pos_algorithm;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (template_id),
GNUNET_PQ_query_param_string (td->template_description),
(NULL == td->pos_key)
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_string (td->pos_key),
GNUNET_PQ_query_param_uint32 (&pos32),
TALER_PQ_query_param_json (td->template_contract),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_template",
params);
}
/**
* Update details about a particular template.
*
* @param cls closure
* @param instance_id instance to update template for
* @param template_id template to update
* @param td update to the template details on success, can be NULL
* (in that case we only want to check if the template exists)
* @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the template
* does not yet exist.
*/
static enum GNUNET_DB_QueryStatus
postgres_update_template (void *cls,
const char *instance_id,
const char *template_id,
const struct TALER_MERCHANTDB_TemplateDetails *td)
{
struct PostgresClosure *pg = cls;
uint32_t pos32 = (uint32_t) td->pos_algorithm;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (template_id),
GNUNET_PQ_query_param_string (td->template_description),
(NULL == td->pos_key)
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_string (td->pos_key),
GNUNET_PQ_query_param_uint32 (&pos32),
TALER_PQ_query_param_json (td->template_contract),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_template",
params);
}
/**
* Context used for postgres_lookup_template().
*/
struct LookupTemplateContext
{
/**
* Function to call with the results.
*/
TALER_MERCHANTDB_TemplatesCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Did database result extraction fail?
*/
bool extract_failed;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about template.
*
* @param[in,out] cls of type `struct LookupTemplateContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_templates_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupTemplateContext *tlc = cls;
for (unsigned int i = 0; i < num_results; i++)
{
char *template_id;
char *template_description;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("template_id",
&template_id),
GNUNET_PQ_result_spec_string ("template_description",
&template_description),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
tlc->extract_failed = true;
return;
}
tlc->cb (tlc->cb_cls,
template_id,
template_description);
GNUNET_PQ_cleanup_result (rs);
}
}
/**
* Lookup all of the templates the given instance has configured.
*
* @param cls closure
* @param instance_id instance to lookup template for
* @param cb function to call on all template found
* @param cb_cls closure for @a cb
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_templates (void *cls,
const char *instance_id,
TALER_MERCHANTDB_TemplatesCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupTemplateContext tlc = {
.cb = cb,
.cb_cls = cb_cls,
/* Can be overwritten by the lookup_template_cb */
.extract_failed = false,
};
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_templates",
params,
&lookup_templates_cb,
&tlc);
/* If there was an error inside lookup_template_cb, return a hard error. */
if (tlc.extract_failed)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Lookup details about a particular template.
*
* @param cls closure
* @param instance_id instance to lookup template for
* @param template_id template to lookup
* @param[out] td set to the template details on success, can be NULL
* (in that case we only want to check if the template exists)
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_template (void *cls,
const char *instance_id,
const char *template_id,
struct TALER_MERCHANTDB_TemplateDetails *td)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (template_id),
GNUNET_PQ_query_param_end
};
if (NULL == td)
{
struct GNUNET_PQ_ResultSpec rs_null[] = {
GNUNET_PQ_result_spec_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_template",
params,
rs_null);
}
else
{
uint32_t pos32;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("template_description",
&td->template_description),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("pos_key",
&td->pos_key),
NULL),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_uint32 ("pos_algorithm",
&pos32),
NULL),
TALER_PQ_result_spec_json ("template_contract",
&td->template_contract),
GNUNET_PQ_result_spec_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
td->pos_key = NULL;
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_template",
params,
rs);
td->pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos32;
return qs;
}
}
/**
* Delete information about a webhook.
*
* @param cls closure
* @param instance_id instance to delete webhook of
* @param webhook_id webhook to delete
* @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
* if webhook unknown.
*/
static enum GNUNET_DB_QueryStatus
postgres_delete_webhook (void *cls,
const char *instance_id,
const char *webhook_id)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (webhook_id),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"delete_webhook",
params);
}
/**
* Insert details about a particular webhook.
*
* @param cls closure
* @param instance_id instance to insert webhook for
* @param webhook_id webhook identifier of webhook to insert
* @param wb the webhook details to insert
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_insert_webhook (void *cls,
const char *instance_id,
const char *webhook_id,
const struct TALER_MERCHANTDB_WebhookDetails *wb)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (webhook_id),
GNUNET_PQ_query_param_string (wb->event_type),
GNUNET_PQ_query_param_string (wb->url),
GNUNET_PQ_query_param_string (wb->http_method),
(NULL == wb->header_template)
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_string (wb->header_template),
(NULL == wb->body_template)
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_string (wb->body_template),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_webhook",
params);
}
/**
* Update details about a particular webhook.
*
* @param cls closure
* @param instance_id instance to update template for
* @param webhook_id webhook to update
* @param wb update to the webhook details on success, can be NULL
* (in that case we only want to check if the webhook exists)
* @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the webhook
* does not yet exist.
*/
static enum GNUNET_DB_QueryStatus
postgres_update_webhook (void *cls,
const char *instance_id,
const char *webhook_id,
const struct TALER_MERCHANTDB_WebhookDetails *wb)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (webhook_id),
GNUNET_PQ_query_param_string (wb->event_type),
GNUNET_PQ_query_param_string (wb->url),
GNUNET_PQ_query_param_string (wb->http_method),
(NULL == wb->header_template)
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_string (wb->header_template),
(NULL == wb->body_template)
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_string (wb->body_template),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_webhook",
params);
}
/**
* Context used for postgres_lookup_webhook().
*/
struct LookupWebhookContext
{
/**
* Function to call with the results.
*/
TALER_MERCHANTDB_WebhooksCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Did database result extraction fail?
*/
bool extract_failed;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about webhook.
*
* @param[in,out] cls of type `struct LookupWebhookContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_webhooks_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupWebhookContext *wlc = cls;
for (unsigned int i = 0; i < num_results; i++)
{
char *webhook_id;
char *event_type;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("webhook_id",
&webhook_id),
GNUNET_PQ_result_spec_string ("event_type",
&event_type),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
wlc->extract_failed = true;
return;
}
wlc->cb (wlc->cb_cls,
webhook_id,
event_type);
GNUNET_PQ_cleanup_result (rs);
}
}
/**
* Lookup all of the webhooks the given instance has configured.
*
* @param cls closure
* @param instance_id instance to lookup webhook for
* @param cb function to call on all webhook found
* @param cb_cls closure for @a cb
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_webhooks (void *cls,
const char *instance_id,
TALER_MERCHANTDB_WebhooksCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupWebhookContext wlc = {
.cb = cb,
.cb_cls = cb_cls,
/* Can be overwritten by the lookup_webhook_cb */
.extract_failed = false,
};
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_webhooks",
params,
&lookup_webhooks_cb,
&wlc);
/* If there was an error inside lookup_webhook_cb, return a hard error. */
if (wlc.extract_failed)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Lookup details about a particular webhook.
*
* @param cls closure
* @param instance_id instance to lookup webhook for
* @param webhook_id webhook to lookup
* @param[out] wb set to the webhook details on success, can be NULL
* (in that case we only want to check if the webhook exists)
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_webhook (void *cls,
const char *instance_id,
const char *webhook_id,
struct TALER_MERCHANTDB_WebhookDetails *wb)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (webhook_id),
GNUNET_PQ_query_param_end
};
if (NULL == wb)
{
struct GNUNET_PQ_ResultSpec rs_null[] = {
GNUNET_PQ_result_spec_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_webhook",
params,
rs_null);
}
else
{
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_string ("event_type",
&wb->event_type),
GNUNET_PQ_result_spec_string ("url",
&wb->url),
GNUNET_PQ_result_spec_string ("http_method",
&wb->http_method),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("header_template",
&wb->header_template),
NULL),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("body_template",
&wb->body_template),
NULL),
GNUNET_PQ_result_spec_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"lookup_webhook",
params,
rs);
}
}
/**
* Context used for postgres_lookup_webhook().
*/
struct LookupWebhookDetailContext
{
/**
* Function to call with the results.
*/
TALER_MERCHANTDB_WebhookDetailCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Did database result extraction fail?
*/
bool extract_failed;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about webhook.
*
* @param[in,out] cls of type `struct LookupPendingWebhookContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_webhook_by_event_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupWebhookDetailContext *wlc = cls;
for (unsigned int i = 0; i < num_results; i++)
{
uint64_t webhook_serial;
char *event_type;
char *url;
char *http_method;
char *header_template;
char *body_template;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("webhook_serial",
&webhook_serial),
GNUNET_PQ_result_spec_string ("event_type",
&event_type),
GNUNET_PQ_result_spec_string ("url",
&url),
GNUNET_PQ_result_spec_string ("http_method",
&http_method),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("header_template",
&header_template),
NULL),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("body_template",
&body_template),
NULL),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
wlc->extract_failed = true;
return;
}
wlc->cb (wlc->cb_cls,
webhook_serial,
event_type,
url,
http_method,
header_template,
body_template);
GNUNET_PQ_cleanup_result (rs);
}
}
/**
* Lookup webhook by event
*
* @param cls closure
* @param instance_id instance to lookup webhook for
* @param event_type event that we need to put in the pending webhook
* @param[out] cb set to the webhook details on success
* @param cb_cls callback closure
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_lookup_webhook_by_event (void *cls,
const char *instance_id,
const char *event_type,
TALER_MERCHANTDB_WebhookDetailCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupWebhookDetailContext wlc = {
.cb = cb,
.cb_cls = cb_cls,
.extract_failed = false,
};
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_string (event_type),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_webhook_by_event",
params,
&lookup_webhook_by_event_cb,
&wlc);
if (wlc.extract_failed)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Insert webhook in the pending webhook.
*
* @param cls closure
* @param instance_id instance to insert webhook for
* @param webhook_serial webhook to insert in the pending webhook
* @param url to make the request to
* @param http_method for the webhook
* @param header of the webhook
* @param body of the webhook
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_insert_pending_webhook (void *cls,
const char *instance_id,
uint64_t webhook_serial,
const char *url,
const char *http_method,
const char *header,
const char *body)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_uint64 (&webhook_serial),
GNUNET_PQ_query_param_string (url),
GNUNET_PQ_query_param_string (http_method),
NULL == header
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_string (header),
NULL == body
? GNUNET_PQ_query_param_null ()
: GNUNET_PQ_query_param_string (body),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_pending_webhook",
params);
}
/**
* Context used for postgres_lookup_future_webhook().
*/
struct LookupPendingWebhookContext
{
/**
* Function to call with the results.
*/
TALER_MERCHANTDB_PendingWebhooksCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Did database result extraction fail?
*/
bool extract_failed;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results about webhook.
*
* @param[in,out] cls of type `struct LookupPendingWebhookContext *`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
lookup_pending_webhooks_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct LookupPendingWebhookContext *pwlc = cls;
for (unsigned int i = 0; i < num_results; i++)
{
uint64_t webhook_pending_serial;
struct GNUNET_TIME_Absolute next_attempt;
uint32_t retries;
char *url;
char *http_method;
char *header = NULL;
char *body = NULL;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("webhook_pending_serial",
&webhook_pending_serial),
GNUNET_PQ_result_spec_absolute_time ("next_attempt",
&next_attempt),
GNUNET_PQ_result_spec_uint32 ("retries",
&retries),
GNUNET_PQ_result_spec_string ("url",
&url),
GNUNET_PQ_result_spec_string ("http_method",
&http_method),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("header",
&header),
NULL),
GNUNET_PQ_result_spec_allow_null (
GNUNET_PQ_result_spec_string ("body",
&body),
NULL),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
pwlc->extract_failed = true;
return;
}
pwlc->cb (pwlc->cb_cls,
webhook_pending_serial,
next_attempt,
retries,
url,
http_method,
header,
body);
GNUNET_PQ_cleanup_result (rs);
}
}
/**
* Lookup the webhook that need to be send in priority.
* send.
*
* @param cls closure
* @param cb pending webhook callback
* @param cb_cls callback closure
*/
// WHERE next_attempt <= now ORDER BY next_attempt ASC
static enum GNUNET_DB_QueryStatus
postgres_lookup_pending_webhooks (void *cls,
TALER_MERCHANTDB_PendingWebhooksCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupPendingWebhookContext pwlc = {
.cb = cb,
.cb_cls = cb_cls,
.extract_failed = false,
};
struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
struct GNUNET_PQ_QueryParam params_null[] = {
GNUNET_PQ_query_param_absolute_time (&now),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_pending_webhooks",
params_null,
&lookup_pending_webhooks_cb,
&pwlc);
if (pwlc.extract_failed)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Lookup future webhook in the pending webhook that need to be send.
* With that we can know how long the system can 'sleep'.
*
* @param cls closure
* @param cb pending webhook callback
* @param cb_cls callback closure
*/
// ORDER BY next_attempt ASC LIMIT 1
static enum GNUNET_DB_QueryStatus
postgres_lookup_future_webhook (void *cls,
TALER_MERCHANTDB_PendingWebhooksCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupPendingWebhookContext pwlc = {
.cb = cb,
.cb_cls = cb_cls,
.extract_failed = false,
};
struct GNUNET_PQ_QueryParam params_null[] = {
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_future_webhook",
params_null,
&lookup_pending_webhooks_cb,
&pwlc);
if (pwlc.extract_failed)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Lookup all the webhooks in the pending webhook.
* Use by the administrator
*
* @param cls closure
* @param instance_id to lookup webhooks for this instance particularly
* @param min_row to see the list of the pending webhook that it is started with this minimum row.
* @param max_results to see the list of the pending webhook that it is end with this max results.
* @param cb pending webhook callback
* @param cb_cls callback closure
*/
// WHERE webhook_pending_serial > min_row ORDER BY webhook_pending_serial ASC LIMIT max_results
static enum GNUNET_DB_QueryStatus
postgres_lookup_all_webhooks (void *cls,
const char *instance_id,
uint64_t min_row,
uint32_t max_results,
TALER_MERCHANTDB_PendingWebhooksCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct LookupPendingWebhookContext pwlc = {
.cb = cb,
.cb_cls = cb_cls,
.extract_failed = false,
};
uint64_t max_results64 = max_results;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (instance_id),
GNUNET_PQ_query_param_uint64 (&min_row),
GNUNET_PQ_query_param_uint64 (&max_results64),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
check_connection (pg);
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"lookup_all_webhooks",
params,
&lookup_pending_webhooks_cb,
&pwlc);
if (pwlc.extract_failed)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Update the pending webhook. It is use if the webhook can't be send.
*
* @param cls closure
* @param webhook_pending_serial pending_webhook that need to be update
* @param next_attempt when to try the webhook next
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_update_pending_webhook (void *cls,
uint64_t webhook_pending_serial,
struct GNUNET_TIME_Absolute next_attempt)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&webhook_pending_serial),
GNUNET_PQ_query_param_absolute_time (&next_attempt),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"update_pending_webhook",
params);
}
/**
* Delete a webhook in the pending webhook after the
* webhook was completed successfully.
*
* @param cls closure
* @param webhook_pending_serial identifies the row that needs to be deleted in the pending webhook table
* @return database result code
*/
static enum GNUNET_DB_QueryStatus
postgres_delete_pending_webhook (void *cls,
uint64_t webhook_pending_serial)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&webhook_pending_serial),
GNUNET_PQ_query_param_end
};
check_connection (pg);
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"delete_pending_webhook",
params);
}
/**
* Establish connection to the database.
*
* @param cls plugin context
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
*/
static int
postgres_connect (void *cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_PreparedStatement ps[] = {
GNUNET_PQ_make_prepare ("end_transaction",
"COMMIT"),
/* for postgres_lookup_deposits_by_order() */
GNUNET_PQ_make_prepare ("lookup_deposits_by_order",
"SELECT"
" deposit_serial"
",exchange_url"
",h_wire"
",amount_with_fee"
",deposit_fee"
",coin_pub"
" FROM merchant_deposits"
" JOIN merchant_accounts USING (account_serial)"
" WHERE order_serial=$1"),
/* for postgres_lookup_transfer_details_by_order() */
GNUNET_PQ_make_prepare ("lookup_transfer_details_by_order",
"SELECT"
" md.deposit_serial"
",md.exchange_url"
",mt.wtid"
",exchange_deposit_value"
",exchange_deposit_fee"
",deposit_timestamp"
",mt.confirmed AS transfer_confirmed"
" FROM merchant_transfer_to_coin"
" JOIN merchant_deposits AS md USING (deposit_serial)"
" JOIN merchant_transfers AS mt USING (credit_serial)"
" WHERE deposit_serial IN"
" (SELECT deposit_serial"
" FROM merchant_deposits"
" WHERE order_serial=$1)"),
/* for postgres_mark_order_wired() */
GNUNET_PQ_make_prepare ("mark_order_wired",
"UPDATE merchant_contract_terms SET"
" wired=true"
" WHERE order_serial=$1"),
/* for postgres_lookup_refunds_detailed() */
GNUNET_PQ_make_prepare ("lookup_refunds_detailed",
"SELECT"
" refund_serial"
",refund_timestamp"
",coin_pub"
",merchant_deposits.exchange_url"
",rtransaction_id"
",reason"
",refund_amount"
",merchant_refund_proofs.exchange_sig IS NULL AS pending"
" FROM merchant_refunds"
" JOIN merchant_deposits USING (order_serial, coin_pub)"
" LEFT JOIN merchant_refund_proofs USING (refund_serial)"
" WHERE order_serial="
" (SELECT order_serial"
" FROM merchant_contract_terms"
" WHERE h_contract_terms=$2"
" AND merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1))"),
/* for postgres_insert_refund_proof() */
GNUNET_PQ_make_prepare ("insert_refund_proof",
"INSERT INTO merchant_refund_proofs"
"(refund_serial"
",exchange_sig"
",signkey_serial)"
"SELECT $1, $2, signkey_serial"
" FROM merchant_exchange_signing_keys"
" WHERE exchange_pub=$3"
" ORDER BY start_date DESC"
" LIMIT 1"),
/* for postgres_lookup_refund_proof() */
GNUNET_PQ_make_prepare ("lookup_refund_proof",
"SELECT"
" merchant_exchange_signing_keys.exchange_pub"
",exchange_sig"
" FROM merchant_refund_proofs"
" JOIN merchant_exchange_signing_keys"
" USING (signkey_serial)"
" WHERE"
" refund_serial=$1"),
/* for postgres_lookup_order_by_fulfillment() */
GNUNET_PQ_make_prepare ("lookup_order_by_fulfillment",
"SELECT"
" order_id"
" FROM merchant_contract_terms"
" WHERE fulfillment_url=$2"
" AND session_id=$3"
" AND merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* for postgres_delete_transfer() */
GNUNET_PQ_make_prepare ("delete_transfer",
"DELETE FROM merchant_transfers"
" WHERE"
" credit_serial=$2"
" AND account_serial IN "
" (SELECT account_serial "
" FROM merchant_accounts"
" WHERE merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1))"),
/* for postgres_check_transfer_exists() */
GNUNET_PQ_make_prepare ("check_transfer_exists",
"SELECT"
" 1"
" FROM merchant_transfers"
" JOIN merchant_accounts"
" USING (account_serial)"
" WHERE"
" credit_serial=$2"
" AND"
" merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* for postgres_lookup_account() */
GNUNET_PQ_make_prepare ("lookup_account",
"SELECT"
" account_serial"
" FROM merchant_accounts"
" WHERE payto_uri=$2"
" AND merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* for postgres_lookup_wire_fee() */
GNUNET_PQ_make_prepare ("lookup_wire_fee",
"SELECT"
" wire_fee"
",closing_fee"
",start_date"
",end_date"
",master_sig"
" FROM merchant_exchange_wire_fees"
" WHERE master_pub=$1"
" AND h_wire_method=$2"
" AND start_date <= $3"
" AND end_date > $3"),
/* for postgres_lookup_deposits_by_contract_and_coin() */
GNUNET_PQ_make_prepare ("lookup_deposits_by_contract_and_coin",
"SELECT"
" exchange_url"
",amount_with_fee"
",deposit_fee"
",refund_fee"
",wire_fee"
",h_wire"
",deposit_timestamp"
",refund_deadline"
",exchange_sig"
",exchange_pub"
" FROM merchant_contract_terms"
" JOIN merchant_deposits USING (order_serial)"
" JOIN merchant_exchange_signing_keys USING (signkey_serial)"
" JOIN merchant_accounts USING (account_serial)"
" WHERE h_contract_terms=$2"
" AND coin_pub=$3"
" AND merchant_contract_terms.merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* for postgres_lookup_transfer() */
GNUNET_PQ_make_prepare ("lookup_transfer",
"SELECT"
" mt.credit_amount AS credit_amount"
",mts.credit_amount AS exchange_amount"
",wire_fee"
",execution_time"
",verified"
" FROM merchant_transfers mt"
" JOIN merchant_accounts USING (account_serial)"
" JOIN merchant_instances USING (merchant_serial)"
" LEFT JOIN merchant_transfer_signatures mts USING (credit_serial)"
" WHERE wtid=$2"
" AND exchange_url=$1"
" AND merchant_id=$3;"),
/* for postgres_lookup_transfer_summary() */
GNUNET_PQ_make_prepare ("lookup_transfer_summary",
"SELECT"
" order_id"
",exchange_deposit_value"
",exchange_deposit_fee"
" FROM merchant_transfers"
" JOIN merchant_transfer_to_coin USING (credit_serial)"
" JOIN merchant_deposits USING (deposit_serial)"
" JOIN merchant_contract_terms USING (order_serial)"
" WHERE wtid=$2"
" AND merchant_transfers.exchange_url=$1"),
/* for postgres_lookup_transfer_details() */
GNUNET_PQ_make_prepare ("lookup_transfer_details",
"SELECT"
" merchant_contract_terms.h_contract_terms"
",merchant_transfer_to_coin.offset_in_exchange_list"
",merchant_deposits.coin_pub"
",exchange_deposit_value"
",exchange_deposit_fee"
" FROM merchant_transfer_to_coin"
" JOIN merchant_deposits USING (deposit_serial)"
" JOIN merchant_contract_terms USING (order_serial)"
" JOIN merchant_transfers USING (credit_serial)"
" WHERE merchant_transfers.wtid=$2"
" AND merchant_transfers.exchange_url=$1"),
/* For postgres_insert_reserve() */
GNUNET_PQ_make_prepare ("insert_reserve_key",
"INSERT INTO merchant_reward_reserve_keys"
"(reserve_serial"
",reserve_priv"
",exchange_url"
",master_pub"
")"
"SELECT reserve_serial, $3, $4, $5"
" FROM merchant_reward_reserves"
" WHERE reserve_pub=$2"
" AND merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* For postgres_lookup_pending_reserves() */
GNUNET_PQ_make_prepare ("lookup_pending_reserves",
"SELECT"
" reserve_pub"
",merchant_id"
",exchange_url"
",merchant_initial_balance"
" FROM merchant_reward_reserves"
" JOIN merchant_instances USING (merchant_serial)"
" JOIN merchant_reward_reserve_keys USING (reserve_serial)"
" WHERE exchange_initial_balance=CAST((0,0) AS taler_amount)"),
/* For postgres_lookup_reserve() */
GNUNET_PQ_make_prepare ("lookup_reserve",
"SELECT"
" creation_time"
",expiration"
",merchant_initial_balance"
",exchange_initial_balance"
",rewards_committed"
",rewards_picked_up"
",reserve_priv IS NOT NULL AS active"
",exchange_url"
",master_pub"
" FROM merchant_reward_reserves"
" FULL OUTER JOIN merchant_reward_reserve_keys USING (reserve_serial)"
" WHERE reserve_pub = $2"
" AND merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* For postgres_lookup_reserve() */
GNUNET_PQ_make_prepare ("lookup_reserve_rewards",
"SELECT"
" justification"
",reward_id"
",amount"
" FROM merchant_rewards"
" WHERE reserve_serial ="
" (SELECT reserve_serial"
" FROM merchant_reward_reserves"
" WHERE reserve_pub=$2"
" AND merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1))"),
/* for postgres_delete_reserve() */
GNUNET_PQ_make_prepare ("delete_reserve",
"DELETE"
" FROM merchant_reward_reserve_keys"
" WHERE reserve_serial="
" (SELECT reserve_serial"
" FROM merchant_reward_reserves"
" WHERE reserve_pub=$2"
" AND merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1))"),
/* for postgres_purge_reserve() */
GNUNET_PQ_make_prepare ("purge_reserve",
"DELETE"
" FROM merchant_reward_reserves"
" WHERE reserve_pub=$2"
" AND merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* For postgres_lookup_pickup() */
GNUNET_PQ_make_prepare ("lookup_pickup",
"SELECT"
" exchange_url"
",reserve_priv"
",pickup_serial"
" FROM merchant_reward_pickups"
" JOIN merchant_rewards USING (reward_serial)"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" JOIN merchant_reward_reserve_keys USING (reserve_serial)"
" WHERE pickup_id = $3"
" AND reward_id = $2"
" AND merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* For postgres_lookup_pickup() */
GNUNET_PQ_make_prepare ("lookup_pickup_signatures",
"SELECT"
" coin_offset"
",blind_sig"
" FROM merchant_reward_pickup_signatures"
" WHERE pickup_serial = $1"),
/* For postgres_lookup_reward() */
GNUNET_PQ_make_prepare ("lookup_reward",
"SELECT"
" amount"
",picked_up"
",merchant_rewards.expiration"
",exchange_url"
",next_url"
",reserve_priv"
" FROM merchant_rewards"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" JOIN merchant_reward_reserve_keys USING (reserve_serial)"
" WHERE reward_id = $2"
" AND merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* For postgres_lookup_reward() */
GNUNET_PQ_make_prepare ("lookup_rewards_inc",
"SELECT"
" reward_serial"
",reward_id"
",amount"
",CAST($4 as BIGINT)" /* otherwise $4 is unused and Postgres unhappy */
",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
" FROM merchant_rewards"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" WHERE merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND"
" reward_serial > $3"
" ORDER BY reward_serial ASC"
" LIMIT $2"),
GNUNET_PQ_make_prepare ("lookup_rewards_dec",
"SELECT"
" reward_serial"
",reward_id"
",amount"
",CAST($4 as BIGINT)" /* otherwise $4 is unused and Postgres unhappy */
",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
" FROM merchant_rewards"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" WHERE merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND"
" reward_serial < $3"
" ORDER BY reward_serial DESC"
" LIMIT $2"),
GNUNET_PQ_make_prepare ("lookup_rewards_inc_expired",
"SELECT"
" reward_serial"
",reward_id"
",amount"
" FROM merchant_rewards"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" WHERE merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND"
" reward_serial > $3"
" AND"
" CAST($5 as BOOL) = (merchant_rewards.expiration < $4)"
" ORDER BY reward_serial ASC"
" LIMIT $2"),
GNUNET_PQ_make_prepare ("lookup_rewards_dec_expired",
"SELECT"
" reward_serial"
",reward_id"
",amount"
" FROM merchant_rewards"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" WHERE merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND"
" reward_serial < $3"
" AND"
" CAST($5 as BOOL) = (merchant_rewards.expiration < $4)"
" ORDER BY reward_serial DESC"
" LIMIT $2"),
/* for postgres_lookup_reward_details() */
GNUNET_PQ_make_prepare ("lookup_reward_details",
"SELECT"
" reward_serial"
",amount"
",picked_up"
",justification"
",merchant_rewards.expiration"
",reserve_pub"
" FROM merchant_rewards"
" JOIN merchant_reward_reserves USING (reserve_serial)"
" WHERE reward_id = $2"
" AND merchant_serial ="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"),
/* for postgres_lookup_reward_details() */
GNUNET_PQ_make_prepare ("lookup_pickup_details",
"SELECT"
" pickup_id"
",amount"
",COUNT(blind_sig) AS num_planchets"
" FROM merchant_reward_pickups"
" JOIN merchant_reward_pickup_signatures USING (pickup_serial)"
" WHERE reward_serial = $1"
" GROUP BY pickup_serial"),
/* for postgres_insert_pickup_blind_signature() */
GNUNET_PQ_make_prepare ("insert_pickup_blind_signature",
"INSERT INTO merchant_reward_pickup_signatures"
"(pickup_serial"
",coin_offset"
",blind_sig"
") "
"SELECT"
" pickup_serial, $2, $3"
" FROM merchant_reward_pickups"
" WHERE pickup_id=$1"),
/* for postgres_lookup_templates() */
GNUNET_PQ_make_prepare ("lookup_templates",
"SELECT"
" template_id"
",template_description"
" FROM merchant_template"
" JOIN merchant_instances"
" USING (merchant_serial)"
" WHERE merchant_instances.merchant_id=$1"),
/* for postgres_lookup_template() */
GNUNET_PQ_make_prepare ("lookup_template",
"SELECT"
" template_description"
",pos_key"
",pos_algorithm"
",template_contract"
" FROM merchant_template"
" JOIN merchant_instances"
" USING (merchant_serial)"
" WHERE merchant_instances.merchant_id=$1"
" AND merchant_template.template_id=$2"),
/* for postgres_delete_template() */
GNUNET_PQ_make_prepare ("delete_template",
"DELETE"
" FROM merchant_template"
" WHERE merchant_template.merchant_serial="
" (SELECT merchant_serial "
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND merchant_template.template_id=$2"),
/* for postgres_insert_template() */
GNUNET_PQ_make_prepare ("insert_template",
"INSERT INTO merchant_template"
"(merchant_serial"
",template_id"
",template_description"
",pos_key"
",pos_algorithm"
",template_contract"
")"
" SELECT merchant_serial,"
" $2, $3, $4, $5, $6"
" FROM merchant_instances"
" WHERE merchant_id=$1"),
/* for postgres_update_template() */
GNUNET_PQ_make_prepare ("update_template",
"UPDATE merchant_template SET"
" template_description=$3"
",pos_key=$4"
",pos_algorithm=$5"
",template_contract=$6"
" WHERE merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND template_id=$2"),
/* for postgres_lookup_webhooks() */
GNUNET_PQ_make_prepare ("lookup_webhooks",
"SELECT"
" webhook_id"
",event_type"
" FROM merchant_webhook"
" JOIN merchant_instances"
" USING (merchant_serial)"
" WHERE merchant_instances.merchant_id=$1"),
/* for postgres_lookup_webhook() */
GNUNET_PQ_make_prepare ("lookup_webhook",
"SELECT"
" event_type"
",url"
",http_method"
",header_template"
",body_template"
" FROM merchant_webhook"
" JOIN merchant_instances"
" USING (merchant_serial)"
" WHERE merchant_instances.merchant_id=$1"
" AND merchant_webhook.webhook_id=$2"),
/* for postgres_delete_webhook() */
GNUNET_PQ_make_prepare ("delete_webhook",
"DELETE"
" FROM merchant_webhook"
" WHERE merchant_webhook.merchant_serial="
" (SELECT merchant_serial "
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND merchant_webhook.webhook_id=$2"),
/* for postgres_insert_webhook() */
GNUNET_PQ_make_prepare ("insert_webhook",
"INSERT INTO merchant_webhook"
"(merchant_serial"
",webhook_id"
",event_type"
",url"
",http_method"
",header_template"
",body_template"
")"
" SELECT merchant_serial,"
" $2, $3, $4, $5, $6, $7"
" FROM merchant_instances"
" WHERE merchant_id=$1"),
/* for postgres_update_webhook() */
GNUNET_PQ_make_prepare ("update_webhook",
"UPDATE merchant_webhook SET"
" event_type=$3"
",url=$4"
",http_method=$5"
",header_template=$6"
",body_template=$7"
" WHERE merchant_serial="
" (SELECT merchant_serial"
" FROM merchant_instances"
" WHERE merchant_id=$1)"
" AND webhook_id=$2"),
/* for postgres_lookup_webhook_by_event() */
GNUNET_PQ_make_prepare ("lookup_webhook_by_event",
"SELECT"
" webhook_serial"
",event_type"
",url"
",http_method"
",header_template"
",body_template"
" FROM merchant_webhook"
" JOIN merchant_instances"
" USING (merchant_serial)"
" WHERE merchant_instances.merchant_id=$1"
" AND event_type=$2"),
/* for postgres_delete_pending_webhook() */
GNUNET_PQ_make_prepare ("delete_pending_webhook",
"DELETE"
" FROM merchant_pending_webhooks"
" WHERE webhook_pending_serial=$1"),
/* for postgres_insert_pending_webhook() */
GNUNET_PQ_make_prepare ("insert_pending_webhook",
"INSERT INTO merchant_pending_webhooks"
"(merchant_serial"
",webhook_serial"
",url"
",http_method"
",header"
",body"
")"
" SELECT mi.merchant_serial,"
" $2, $3, $4, $5, $6"
" FROM merchant_instances mi"
" WHERE mi.merchant_id=$1"),
/* for postgres_update_pending_webhook() */
GNUNET_PQ_make_prepare ("update_pending_webhook",
"UPDATE merchant_pending_webhooks SET"
" retries=retries+1"
",next_attempt=$2"
" WHERE webhook_pending_serial=$1"),
/* for postgres_lookup_pending_webhooks() */
GNUNET_PQ_make_prepare ("lookup_pending_webhooks",
"SELECT"
" webhook_pending_serial"
",next_attempt"
",retries"
",url"
",http_method"
",header"
",body"
" FROM merchant_pending_webhooks"
" WHERE next_attempt <= $1"
" ORDER BY next_attempt ASC"
),
/* for postgres_lookup_future_webhook() */
GNUNET_PQ_make_prepare ("lookup_future_webhook",
"SELECT"
" webhook_pending_serial"
",next_attempt"
",retries"
",url"
",http_method"
",header"
",body"
" FROM merchant_pending_webhooks"
" ORDER BY next_attempt ASC LIMIT 1"
),
/* for postgres_lookup_all_webhooks() */
GNUNET_PQ_make_prepare ("lookup_all_webhooks",
" SELECT"
" webhook_pending_serial"
",next_attempt"
",retries"
",url"
",http_method"
",header"
",body"
" FROM merchant_pending_webhooks"
" JOIN merchant_instances"
" USING (merchant_serial)"
" WHERE merchant_instances.merchant_id=$1"
" AND webhook_pending_serial > $2"
" ORDER BY webhook_pending_serial"
" ASC LIMIT $3"),
GNUNET_PQ_PREPARED_STATEMENT_END
};
struct GNUNET_PQ_ExecuteStatement es[] = {
GNUNET_PQ_make_try_execute ("SET search_path TO merchant;"),
GNUNET_PQ_EXECUTE_STATEMENT_END
};
pg->conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
"merchantdb-postgres",
NULL,
es,
ps);
pg->prep_gen++;
if (NULL == pg->conn)
return GNUNET_SYSERR;
return GNUNET_OK;
};
/**
* Initialize Postgres database subsystem.
*
* @param cls a configuration instance
* @return NULL on error, otherwise a `struct TALER_MERCHANTDB_Plugin`
*/
void *
libtaler_plugin_merchantdb_postgres_init (void *cls)
{
const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct PostgresClosure *pg;
struct TALER_MERCHANTDB_Plugin *plugin;
pg = GNUNET_new (struct PostgresClosure);
pg->cfg = cfg;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (cfg,
"merchantdb-postgres",
"SQL_DIR",
&pg->sql_dir))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"merchantdb-postgres",
"SQL_DIR");
GNUNET_free (pg);
return NULL;
}
if (GNUNET_OK !=
TALER_config_get_currency (cfg,
&pg->currency))
{
GNUNET_free (pg->sql_dir);
GNUNET_free (pg);
return NULL;
}
plugin = GNUNET_new (struct TALER_MERCHANTDB_Plugin);
plugin->cls = pg;
plugin->connect = &postgres_connect;
plugin->create_tables = &postgres_create_tables;
plugin->drop_tables = &postgres_drop_tables;
plugin->event_listen = &postgres_event_listen;
plugin->event_listen_cancel = &postgres_event_listen_cancel;
plugin->event_notify = &postgres_event_notify;
plugin->preflight = &postgres_preflight;
plugin->start = &TMH_PG_start;
plugin->start_read_committed = &TMH_PG_start_read_committed;
plugin->rollback = &TMH_PG_rollback;
plugin->commit = &TMH_PG_commit;
plugin->lookup_instance_auth
= &TMH_PG_lookup_instance_auth;
plugin->insert_instance
= &TMH_PG_insert_instance;
plugin->insert_account
= &TMH_PG_insert_account;
plugin->update_account
= &TMH_PG_update_account;
plugin->account_kyc_set_status
= &TMH_PG_account_kyc_set_status;
plugin->account_kyc_get_status
= &TMH_PG_account_kyc_get_status;
plugin->delete_instance_private_key
= &TMH_PG_delete_instance_private_key;
plugin->purge_instance
= &TMH_PG_purge_instance;
plugin->update_instance
= &TMH_PG_update_instance;
plugin->update_instance_auth
= &TMH_PG_update_instance_auth;
plugin->activate_account
= &TMH_PG_activate_account;
plugin->inactivate_account
= &TMH_PG_inactivate_account;
plugin->update_transfer_status
= &TMH_PG_update_transfer_status;
plugin->lookup_products
= &TMH_PG_lookup_products;
plugin->lookup_product
= &TMH_PG_lookup_product;
plugin->delete_product
= &TMH_PG_delete_product;
plugin->insert_product
= &TMH_PG_insert_product;
plugin->update_product
= &TMH_PG_update_product;
plugin->lock_product
= &TMH_PG_lock_product;
plugin->expire_locks
= &TMH_PG_expire_locks;
plugin->delete_order
= &TMH_PG_delete_order;
plugin->lookup_order
= &TMH_PG_lookup_order;
plugin->lookup_order_summary
= &TMH_PG_lookup_order_summary;
plugin->lookup_orders
= &TMH_PG_lookup_orders;
plugin->insert_order
= &TMH_PG_insert_order;
plugin->unlock_inventory
= &TMH_PG_unlock_inventory;
plugin->insert_order_lock
= &TMH_PG_insert_order_lock;
plugin->lookup_contract_terms
= &TMH_PG_lookup_contract_terms;
plugin->lookup_contract_terms2
= &TMH_PG_lookup_contract_terms2;
plugin->insert_contract_terms
= &TMH_PG_insert_contract_terms;
plugin->update_contract_terms
= &TMH_PG_update_contract_terms;
plugin->delete_contract_terms
= &TMH_PG_delete_contract_terms;
plugin->lookup_deposits
= &TMH_PG_lookup_deposits;
plugin->insert_exchange_signkey
= &TMH_PG_insert_exchange_signkey;
plugin->insert_deposit
= &TMH_PG_insert_deposit;
plugin->lookup_refunds
= &TMH_PG_lookup_refunds;
plugin->mark_contract_paid
= &TMH_PG_mark_contract_paid;
plugin->refund_coin
= &TMH_PG_refund_coin;
plugin->lookup_order_status
= &TMH_PG_lookup_order_status;
plugin->lookup_order_status_by_serial
= &TMH_PG_lookup_order_status_by_serial;
plugin->lookup_payment_status
= &TMH_PG_lookup_payment_status;
plugin->lookup_deposits_by_order = &postgres_lookup_deposits_by_order;
plugin->lookup_transfer_details_by_order =
&postgres_lookup_transfer_details_by_order;
plugin->mark_order_wired = &postgres_mark_order_wired;
plugin->increase_refund
= &TMH_PG_increase_refund;
plugin->lookup_refunds_detailed = &postgres_lookup_refunds_detailed;
plugin->insert_refund_proof = &postgres_insert_refund_proof;
plugin->lookup_refund_proof = &postgres_lookup_refund_proof;
plugin->lookup_order_by_fulfillment = &postgres_lookup_order_by_fulfillment;
plugin->delete_transfer = &postgres_delete_transfer;
plugin->check_transfer_exists = &postgres_check_transfer_exists;
plugin->lookup_account = &postgres_lookup_account;
plugin->lookup_wire_fee = &postgres_lookup_wire_fee;
plugin->lookup_deposits_by_contract_and_coin =
&postgres_lookup_deposits_by_contract_and_coin;
plugin->lookup_transfer = &postgres_lookup_transfer;
plugin->set_transfer_status_to_confirmed =
&TMH_PG_set_transfer_status_to_confirmed;
plugin->lookup_transfer_summary = &postgres_lookup_transfer_summary;
plugin->lookup_transfer_details = &postgres_lookup_transfer_details;
plugin->lookup_instances
= &TMH_PG_lookup_instances;
plugin->lookup_instance
= &TMH_PG_lookup_instance;
plugin->lookup_transfers
= &TMH_PG_lookup_transfers;
plugin->update_wirewatch_progress
= &TMH_PG_update_wirewatch_progress;
plugin->select_wirewatch_accounts
= &TMH_PG_select_wirewatch_accounts;
plugin->lookup_reserves
= &TMH_PG_lookup_reserves;
plugin->lookup_pending_reserves = &postgres_lookup_pending_reserves;
plugin->lookup_reserve = &postgres_lookup_reserve;
plugin->delete_reserve = &postgres_delete_reserve;
plugin->purge_reserve = &postgres_purge_reserve;
plugin->lookup_pickup = &postgres_lookup_pickup;
plugin->lookup_reward = &postgres_lookup_reward;
plugin->lookup_rewards = &postgres_lookup_rewards;
plugin->lookup_reward_details = &postgres_lookup_reward_details;
plugin->insert_pickup_blind_signature =
&postgres_insert_pickup_blind_signature;
plugin->select_open_transfers
= &TMH_PG_select_open_transfers;
plugin->insert_exchange_keys
= &TMH_PG_insert_exchange_keys;
plugin->select_exchange_keys
= &TMH_PG_select_exchange_keys;
plugin->insert_deposit_to_transfer
= &TMH_PG_insert_deposit_to_transfer;
plugin->insert_transfer
= &TMH_PG_insert_transfer;
plugin->insert_transfer_details
= &TMH_PG_insert_transfer_details;
plugin->store_wire_fee_by_exchange
= &TMH_PG_store_wire_fee_by_exchange;
plugin->insert_reserve
= &TMH_PG_insert_reserve;
plugin->activate_reserve
= &TMH_PG_activate_reserve;
plugin->authorize_reward
= &TMH_PG_authorize_reward;
plugin->insert_pickup
= &TMH_PG_insert_pickup;
plugin->lookup_templates = &postgres_lookup_templates;
plugin->lookup_template = &postgres_lookup_template;
plugin->delete_template = &postgres_delete_template;
plugin->insert_template = &postgres_insert_template;
plugin->update_template = &postgres_update_template;
plugin->lookup_webhooks = &postgres_lookup_webhooks;
plugin->lookup_webhook = &postgres_lookup_webhook;
plugin->delete_webhook = &postgres_delete_webhook;
plugin->insert_webhook = &postgres_insert_webhook;
plugin->update_webhook = &postgres_update_webhook;
plugin->lookup_webhook_by_event = &postgres_lookup_webhook_by_event;
plugin->lookup_all_webhooks = &postgres_lookup_all_webhooks;
plugin->lookup_future_webhook = &postgres_lookup_future_webhook;
plugin->lookup_pending_webhooks = &postgres_lookup_pending_webhooks;
plugin->delete_pending_webhook = &postgres_delete_pending_webhook;
plugin->insert_pending_webhook = &postgres_insert_pending_webhook;
plugin->update_pending_webhook = &postgres_update_pending_webhook;
plugin->delete_exchange_accounts
= &TMH_PG_delete_exchange_accounts;
plugin->select_accounts_by_exchange
= &TMH_PG_select_accounts_by_exchange;
plugin->insert_exchange_account
= &TMH_PG_insert_exchange_account;
return plugin;
}
/**
* Shutdown Postgres database subsystem.
*
* @param cls a `struct TALER_MERCHANTDB_Plugin`
* @return NULL (always)
*/
void *
libtaler_plugin_merchantdb_postgres_done (void *cls)
{
struct TALER_MERCHANTDB_Plugin *plugin = cls;
struct PostgresClosure *pg = plugin->cls;
if (NULL != pg->conn)
{
GNUNET_PQ_disconnect (pg->conn);
pg->conn = NULL;
}
GNUNET_free (pg->sql_dir);
GNUNET_free (pg->currency);
GNUNET_free (pg);
GNUNET_free (plugin);
return NULL;
}
/* end of plugin_merchantdb_postgres.c */