/*
This file is part of TALER
(C) 2014--2024 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_otp.h"
#include "pg_delete_otp.h"
#include "pg_update_otp.h"
#include "pg_select_otp.h"
#include "pg_select_otp_serial.h"
#include "pg_insert_login_token.h"
#include "pg_delete_login_token.h"
#include "pg_select_login_token.h"
#include "pg_insert_account.h"
#include "pg_update_account.h"
#include "pg_lookup_instances.h"
#include "pg_lookup_transfers.h"
#include "pg_lookup_pending_deposits.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_instance_auth.h"
#include "pg_lookup_otp_devices.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_terms3.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_delete_template.h"
#include "pg_insert_template.h"
#include "pg_update_template.h"
#include "pg_lookup_templates.h"
#include "pg_lookup_template.h"
#include "pg_lookup_deposits.h"
#include "pg_insert_exchange_signkey.h"
#include "pg_insert_deposit.h"
#include "pg_insert_deposit_confirmation.h"
#include "pg_lookup_refunds.h"
#include "pg_mark_contract_paid.h"
#include "pg_select_account_by_uri.h"
#include "pg_refund_coin.h"
#include "pg_lookup_order_status.h"
#include "pg_lookup_order_status_by_serial.h"
#include "pg_lookup_deposits_by_order.h"
#include "pg_lookup_transfer_details_by_order.h"
#include "pg_mark_order_wired.h"
#include "pg_lookup_refunds_detailed.h"
#include "pg_insert_refund_proof.h"
#include "pg_lookup_refund_proof.h"
#include "pg_lookup_order_by_fulfillment.h"
#include "pg_delete_transfer.h"
#include "pg_check_transfer_exists.h"
#include "pg_lookup_account.h"
#include "pg_lookup_wire_fee.h"
#include "pg_lookup_deposits_by_contract_and_coin.h"
#include "pg_lookup_transfer.h"
#include "pg_lookup_transfer_summary.h"
#include "pg_lookup_transfer_details.h"
#include "pg_lookup_webhooks.h"
#include "pg_lookup_webhook.h"
#include "pg_delete_webhook.h"
#include "pg_insert_webhook.h"
#include "pg_update_webhook.h"
#include "pg_lookup_webhook_by_event.h"
#include "pg_delete_pending_webhook.h"
#include "pg_insert_pending_webhook.h"
#include "pg_update_pending_webhook.h"
#include "pg_lookup_pending_webhooks.h"
#include "pg_update_deposit_confirmation_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_select_account.h"
#include "pg_select_accounts.h"
#include "pg_insert_transfer.h"
#include "pg_insert_transfer_details.h"
#include "pg_store_wire_fee_by_exchange.h"
#include "pg_insert_token_family.h"
#include "pg_lookup_token_family.h"
#include "pg_lookup_token_families.h"
#include "pg_delete_token_family.h"
#include "pg_update_token_family.h"
#include "pg_insert_token_family_key.h"
#include "pg_lookup_token_family_key.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
};
enum GNUNET_GenericReturnValue ret;
conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
"merchantdb-postgres",
"merchant-",
es,
NULL);
if (NULL == conn)
return GNUNET_SYSERR;
ret = GNUNET_PQ_exec_sql (conn,
"procedures");
GNUNET_PQ_disconnect (conn);
return ret;
}
/**
* 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
* multiple 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);
}
/**
* Establish connection to the database.
*
* @param cls plugin context
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
*/
static enum GNUNET_GenericReturnValue
postgres_connect (void *cls)
{
struct PostgresClosure *pg = cls;
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,
NULL);
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;
}
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->insert_login_token
= &TMH_PG_insert_login_token;
plugin->delete_login_token
= &TMH_PG_delete_login_token;
plugin->select_login_token
= &TMH_PG_select_login_token;
plugin->select_account_by_uri
= &TMH_PG_select_account_by_uri;
plugin->lookup_instance_auth
= &TMH_PG_lookup_instance_auth;
plugin->insert_instance
= &TMH_PG_insert_instance;
plugin->insert_account
= &TMH_PG_insert_account;
plugin->lookup_otp_devices
= &TMH_PG_lookup_otp_devices;
plugin->delete_template
= &TMH_PG_delete_template;
plugin->insert_template
= &TMH_PG_insert_template;
plugin->update_template
= &TMH_PG_update_template;
plugin->lookup_templates
= &TMH_PG_lookup_templates;
plugin->lookup_template
= &TMH_PG_lookup_template;
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->insert_otp
= &TMH_PG_insert_otp;
plugin->delete_otp
= &TMH_PG_delete_otp;
plugin->update_otp
= &TMH_PG_update_otp;
plugin->select_otp
= &TMH_PG_select_otp;
plugin->select_otp_serial
= &TMH_PG_select_otp_serial;
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->lookup_contract_terms3
= &TMH_PG_lookup_contract_terms3;
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_confirmation
= &TMH_PG_insert_deposit_confirmation;
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_deposits_by_order
= &TMH_PG_lookup_deposits_by_order;
plugin->lookup_transfer_details_by_order
= &TMH_PG_lookup_transfer_details_by_order;
plugin->mark_order_wired
= &TMH_PG_mark_order_wired;
plugin->increase_refund
= &TMH_PG_increase_refund;
plugin->lookup_refunds_detailed
= &TMH_PG_lookup_refunds_detailed;
plugin->insert_refund_proof
= &TMH_PG_insert_refund_proof;
plugin->lookup_refund_proof
= &TMH_PG_lookup_refund_proof;
plugin->lookup_order_by_fulfillment
= &TMH_PG_lookup_order_by_fulfillment;
plugin->delete_transfer
= &TMH_PG_delete_transfer;
plugin->check_transfer_exists
= &TMH_PG_check_transfer_exists;
plugin->lookup_account
= &TMH_PG_lookup_account;
plugin->lookup_wire_fee
= &TMH_PG_lookup_wire_fee;
plugin->lookup_deposits_by_contract_and_coin
= &TMH_PG_lookup_deposits_by_contract_and_coin;
plugin->lookup_transfer
= &TMH_PG_lookup_transfer;
plugin->set_transfer_status_to_confirmed
= &TMH_PG_set_transfer_status_to_confirmed;
plugin->lookup_transfer_summary
= &TMH_PG_lookup_transfer_summary;
plugin->lookup_transfer_details
= &TMH_PG_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->select_account
= &TMH_PG_select_account;
plugin->select_accounts
= &TMH_PG_select_accounts;
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->lookup_webhooks
= &TMH_PG_lookup_webhooks;
plugin->lookup_webhook
= &TMH_PG_lookup_webhook;
plugin->delete_webhook
= &TMH_PG_delete_webhook;
plugin->insert_webhook
= &TMH_PG_insert_webhook;
plugin->update_webhook
= &TMH_PG_update_webhook;
plugin->lookup_pending_deposits
= &TMH_PG_lookup_pending_deposits;
plugin->lookup_webhook_by_event
= &TMH_PG_lookup_webhook_by_event;
plugin->lookup_all_webhooks
= &TMH_PG_lookup_all_webhooks;
plugin->lookup_future_webhook
= &TMH_PG_lookup_future_webhook;
plugin->lookup_pending_webhooks
= &TMH_PG_lookup_pending_webhooks;
plugin->delete_pending_webhook
= &TMH_PG_delete_pending_webhook;
plugin->insert_pending_webhook
= &TMH_PG_insert_pending_webhook;
plugin->update_pending_webhook
= &TMH_PG_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;
plugin->insert_token_family
= &TMH_PG_insert_token_family;
plugin->lookup_token_family
= &TMH_PG_lookup_token_family;
plugin->lookup_token_families
= &TMH_PG_lookup_token_families;
plugin->delete_token_family
= &TMH_PG_delete_token_family;
plugin->update_token_family
= &TMH_PG_update_token_family;
plugin->insert_token_family_key
= &TMH_PG_insert_token_family_key;
plugin->lookup_token_family_key
= &TMH_PG_lookup_token_family_key;
plugin->update_deposit_confirmation_status
= &TMH_PG_update_deposit_confirmation_status;
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);
GNUNET_free (plugin);
return NULL;
}
/* end of plugin_merchantdb_postgres.c */