/*
  This file is part of TALER
  (C) 2014--2025 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 <http://www.gnu.org/licenses/>
*/
/**
 * @file plugin_merchantdb_postgres.c
 * @brief database helper functions for postgres used by the merchant
 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
 * @author Christian Grothoff
 * @author Marcello Stanisci
 * @author Priscilla Huang
 * @author Iván Ávalos
 */
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_pq_lib.h>
#include <taler/taler_util.h>
#include <taler/taler_pq_lib.h>
#include <taler/taler_json_lib.h>
#include <taler/taler_mhd_lib.h>
#include "taler_merchantdb_plugin.h"
#include "pg_helper.h"
#include "pg_gc.h"
#include "pg_insert_otp.h"
#include "pg_get_kyc_status.h"
#include "pg_get_kyc_limits.h"
#include "pg_delete_otp.h"
#include "pg_update_otp.h"
#include "pg_account_kyc_set_failed.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_lookup_login_tokens.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_lookup_categories.h"
#include "pg_lookup_units.h"
#include "pg_select_category.h"
#include "pg_update_category.h"
#include "pg_insert_category.h"
#include "pg_delete_category.h"
#include "pg_select_unit.h"
#include "pg_insert_unit.h"
#include "pg_update_unit.h"
#include "pg_delete_unit.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_lookup_mfa_challenge.h"
#include "pg_solve_mfa_challenge.h"
#include "pg_update_mfa_challenge.h"
#include "pg_create_mfa_challenge.h"
#include "pg_finalize_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_select_category_by_name.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_all_products.h"
#include "pg_lookup_product.h"
#include "pg_lookup_product_image.h"
#include "pg_delete_product.h"
#include "pg_insert_product.h"
#include "pg_update_product.h"
#include "pg_lookup_token_family_keys.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_insert_order_blinded_sigs.h"
#include "pg_unlock_inventory.h"
#include "pg_insert_order_lock.h"
#include "pg_select_order_blinded_sigs.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_expected_transfers.h"
#include "pg_lookup_deposits_by_contract_and_coin.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_lookup_wire_fee.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_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"
#include "pg_insert_spent_token.h"
#include "pg_insert_issued_token.h"
#include "pg_lookup_spent_tokens_by_order.h"
#include "pg_lookup_statistics_amount_by_bucket.h"
#include "pg_lookup_statistics_amount_by_interval.h"
#include "pg_lookup_statistics_counter_by_bucket.h"
#include "pg_lookup_statistics_counter_by_interval.h"
#include "pg_update_donau_instance_receipts_amount.h"
#include "pg_delete_donau_instance.h"
#include "pg_update_contract_session.h"

#ifdef HAVE_DONAU_DONAU_SERVICE_H
#include "donau/donau_service.h"
#include "pg_insert_donau_instance.h"
#include "pg_check_donau_instance.h"
#include "pg_select_donau_instance_by_serial.h"
#include "pg_select_all_donau_instances.h"
#include "pg_select_donau_instances.h"
#include "pg_select_donau_instances_filtered.h"
#include "pg_lookup_donau_keys.h"
#include "pg_lookup_order_charity.h"
#include "pg_upsert_donau_keys.h"
#include "pg_update_donau_instance.h"
#endif

/**
 * 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_cfg2 (pg->cfg,
                                          "merchantdb-postgres",
                                          "merchant-",
                                          es,
                                          NULL, /* prepared statemetns */
                                          GNUNET_PQ_FLAG_CHECK_CURRENT);
  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);

/* declaration to avoid compiler warning */
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->delete_login_token_serial
    = &TMH_PG_delete_login_token_serial;
  plugin->select_login_token
    = &TMH_PG_select_login_token;
  plugin->lookup_login_tokens
    = &TMH_PG_lookup_login_tokens;
  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->finalize_transfer_status
    = &TMH_PG_finalize_transfer_status;
  plugin->lookup_products
    = &TMH_PG_lookup_products;
  plugin->lookup_all_products
    = &TMH_PG_lookup_all_products;
  plugin->lookup_product
    = &TMH_PG_lookup_product;
  plugin->lookup_product_image_by_hash
    = &TMH_PG_lookup_product_image_by_hash;
  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->insert_order_blinded_sigs
    = &TMH_PG_insert_order_blinded_sigs;
  plugin->unlock_inventory
    = &TMH_PG_unlock_inventory;
  plugin->insert_order_lock
    = &TMH_PG_insert_order_lock;
  plugin->select_order_blinded_sigs
    = &TMH_PG_select_order_blinded_sigs;
  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_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->lookup_token_family_keys
    = &TMH_PG_lookup_token_family_keys;
  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->lookup_categories
    = &TMH_PG_lookup_categories;
  plugin->lookup_units
    = &TMH_PG_lookup_units;
  plugin->select_category_by_name
    = &TMH_PG_select_category_by_name;
  plugin->get_kyc_status
    = &TMH_PG_get_kyc_status;
  plugin->account_kyc_set_failed
    = &TMH_PG_account_kyc_set_failed;
  plugin->get_kyc_limits
    = &TMH_PG_get_kyc_limits;
  plugin->select_category
    = &TMH_PG_select_category;
  plugin->select_unit
    = &TMH_PG_select_unit;
  plugin->update_category
    = &TMH_PG_update_category;
  plugin->update_unit
    = &TMH_PG_update_unit;
  plugin->insert_category
    = &TMH_PG_insert_category;
  plugin->insert_unit
    = &TMH_PG_insert_unit;
  plugin->delete_category
    = &TMH_PG_delete_category;
  plugin->delete_unit
    = &TMH_PG_delete_unit;
  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_expected_transfers
    = &TMH_PG_lookup_expected_transfers;
  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->update_contract_session
    = &TMH_PG_update_contract_session;
  plugin->lookup_token_family_key
    = &TMH_PG_lookup_token_family_key;
  plugin->update_deposit_confirmation_status
    = &TMH_PG_update_deposit_confirmation_status;
  plugin->update_donau_instance_receipts_amount
    = &TMH_PG_update_donau_instance_receipts_amount;
  plugin->insert_spent_token
    = &TMH_PG_insert_spent_token;
  plugin->insert_issued_token
    = &TMH_PG_insert_issued_token;
  plugin->lookup_spent_tokens_by_order
    = &TMH_PG_lookup_spent_tokens_by_order;
  plugin->lookup_statistics_amount_by_bucket
    = &TMH_PG_lookup_statistics_amount_by_bucket;
  plugin->lookup_statistics_counter_by_bucket
    = &TMH_PG_lookup_statistics_counter_by_bucket;
  plugin->lookup_statistics_counter_by_interval
    = &TMH_PG_lookup_statistics_counter_by_interval;
  plugin->lookup_statistics_amount_by_interval
    = &TMH_PG_lookup_statistics_amount_by_interval;
  plugin->lookup_mfa_challenge
    = &TMH_PG_lookup_mfa_challenge;
  plugin->solve_mfa_challenge
    = &TMH_PG_solve_mfa_challenge;
  plugin->update_mfa_challenge
    = &TMH_PG_update_mfa_challenge;
  plugin->create_mfa_challenge
    = &TMH_PG_create_mfa_challenge;
  plugin->gc
    = &TMH_PG_gc;
#ifdef HAVE_DONAU_DONAU_SERVICE_H
  plugin->insert_donau_instance
    = &TMH_PG_insert_donau_instance;
  plugin->check_donau_instance
    = &TMH_PG_check_donau_instance;
  plugin->select_donau_instance_by_serial
    = &TMH_PG_select_donau_instance_by_serial;
  plugin->select_all_donau_instances
    = &TMH_PG_select_all_donau_instances;
  plugin->select_donau_instances
    = &TMH_PG_select_donau_instances;
  plugin->select_donau_instances_filtered
    = &TMH_PG_select_donau_instances_filtered;
  plugin->delete_donau_instance
    = &TMH_PG_delete_donau_instance;
  plugin->lookup_donau_keys
    = &TMH_PG_lookup_donau_keys;
  plugin->lookup_order_charity
    = &TMH_PG_lookup_order_charity;
  plugin->upsert_donau_keys
    = &TMH_PG_upsert_donau_keys;
  plugin->update_donau_instance
    = &TMH_PG_update_donau_instance;
#endif
  return plugin;
}


/**
 * Shutdown Postgres database subsystem.
 *
 * @param cls a `struct TALER_MERCHANTDB_Plugin`
 * @return NULL (always)
 */
void *
libtaler_plugin_merchantdb_postgres_done (void *cls);

/* declaration to avoid compiler warning */
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 */
