/* 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 */