merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 91a71aa9e789c7b23226f2178caa86ed6b06db42
parent e484532d9604491ddd5e8b37fb8cd1d7d6202945
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  9 May 2026 14:26:38 +0200

Revert "split DB by instance into schema"

This reverts commit e484532d9604491ddd5e8b37fb8cd1d7d6202945.

Diffstat:
Msrc/backenddb/account_kyc_get_outdated.c | 22++++++++++++++--------
Msrc/backenddb/account_kyc_get_status.c | 36++++++++++++++++--------------------
Msrc/backenddb/account_kyc_set_failed.c | 25+++++++++++++------------
Msrc/backenddb/account_kyc_set_status.c | 27++++++++++++++-------------
Msrc/backenddb/activate_account.c | 26+++++++++++---------------
Msrc/backenddb/check_donau_instance.c | 23++++++++++++-----------
Msrc/backenddb/check_money_pots.c | 31+++++++++++++++----------------
Msrc/backenddb/check_report.c | 15++++++++++-----
Msrc/backenddb/check_transfer_exists.c | 28++++++++++++++++------------
Msrc/backenddb/create_mfa_challenge.c | 43+++++++++++++++++++++----------------------
Msrc/backenddb/delete_category.c | 22+++++++++++-----------
Msrc/backenddb/delete_contract_terms.c | 26+++++++++++++-------------
Msrc/backenddb/delete_donau_instance.c | 19+++++++++----------
Msrc/backenddb/delete_instance_private_key.c | 18+++++++++---------
Msrc/backenddb/delete_login_token.c | 42+++++++++++++++++++++---------------------
Msrc/backenddb/delete_money_pot.c | 22+++++++++++-----------
Msrc/backenddb/delete_order.c | 61+++++++++++++++++++++++++++++++++----------------------------
Msrc/backenddb/delete_otp.c | 22+++++++++++-----------
Msrc/backenddb/delete_pending_webhook.c | 16++++++----------
Msrc/backenddb/delete_product.c | 30+++++++++++++++---------------
Msrc/backenddb/delete_product_group.c | 22+++++++++++-----------
Msrc/backenddb/delete_report.c | 22+++++++++++-----------
Msrc/backenddb/delete_template.c | 22+++++++++++-----------
Msrc/backenddb/delete_token_family.c | 22+++++++++++-----------
Msrc/backenddb/delete_transfer.c | 24++++++++++++++----------
Msrc/backenddb/delete_unit.c | 24+++++++++++-------------
Msrc/backenddb/delete_webhook.c | 22+++++++++++-----------
Msrc/backenddb/expire_locks.c | 59+++++++++++++++++++++++++++++++++++++++++------------------
Msrc/backenddb/finalize_transfer_status.c | 48++++++++++++++++++++++--------------------------
Msrc/backenddb/get_kyc_limits.c | 36++++++++++++++++++------------------
Msrc/backenddb/get_kyc_status.c | 50+++++++++++++++++++++++++-------------------------
Msrc/backenddb/helper.h | 102-------------------------------------------------------------------------------
Msrc/backenddb/inactivate_account.c | 18+++++++-----------
Msrc/backenddb/increase_refund.c | 110++++++++++++++++++++++++++++++++-----------------------------------------------
Msrc/backenddb/increment_money_pots.c | 20++++++++------------
Msrc/backenddb/insert_account.c | 47+++++++++++++++++++++++------------------------
Msrc/backenddb/insert_category.c | 30+++++++++++++++---------------
Msrc/backenddb/insert_contract_terms.c | 73+++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/backenddb/insert_deposit.c | 33+++++++++++++++------------------
Msrc/backenddb/insert_deposit_confirmation.c | 39++++++++++++++++++++++-----------------
Msrc/backenddb/insert_deposit_to_transfer.c | 17+++++++----------
Msrc/backenddb/insert_donau_instance.c | 35+++++++++++++++++++----------------
Msrc/backenddb/insert_instance.c | 28++++++++++------------------
Msrc/backenddb/insert_issued_token.c | 19++++++++-----------
Msrc/backenddb/insert_login_token.c | 31+++++++++++++++----------------
Msrc/backenddb/insert_money_pot.c | 27+++++++++++++--------------
Msrc/backenddb/insert_order.c | 41++++++++++++++++++++---------------------
Msrc/backenddb/insert_order_blinded_sigs.c | 28++++++++++++----------------
Msrc/backenddb/insert_order_lock.c | 99++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/backenddb/insert_otp.c | 32++++++++++++++++----------------
Msrc/backenddb/insert_pending_webhook.c | 33++++++++++++++++-----------------
Msrc/backenddb/insert_product.c | 54++++++++++++++++++++++++++----------------------------
Msrc/backenddb/insert_product_group.c | 27+++++++++++++--------------
Msrc/backenddb/insert_refund_proof.c | 28++++++++++++----------------
Msrc/backenddb/insert_report.c | 41++++++++++++++++++++---------------------
Msrc/backenddb/insert_spent_token.c | 19++++++++-----------
Msrc/backenddb/insert_template.c | 33++++++++++++++++-----------------
Msrc/backenddb/insert_token_family.c | 84++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/backenddb/insert_token_family_key.c | 89++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/backenddb/insert_transfer.c | 26++++++++++++--------------
Msrc/backenddb/insert_transfer_details.c | 38+++++++++++++++++++-------------------
Msrc/backenddb/insert_unclaim_signature.c | 20++++++++------------
Msrc/backenddb/insert_unit.c | 27+++++++++++++--------------
Msrc/backenddb/insert_webhook.c | 34+++++++++++++++++-----------------
Msrc/backenddb/lock_product.c | 100+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/backenddb/lookup_account.c | 28++++++++++++++--------------
Msrc/backenddb/lookup_all_products.c | 81+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/backenddb/lookup_categories.c | 37++++++++++++++++++-------------------
Msrc/backenddb/lookup_categories_by_ids.c | 39+++++++++++++++++++--------------------
Msrc/backenddb/lookup_contract_terms.c | 28++++++++++++++--------------
Msrc/backenddb/lookup_contract_terms2.c | 34+++++++++++++++++-----------------
Msrc/backenddb/lookup_contract_terms3.c | 36++++++++++++++++++------------------
Msrc/backenddb/lookup_custom_units_by_names.c | 42+++++++++++++++++++++---------------------
Msrc/backenddb/lookup_deposits.c | 42+++++++++++++++++++++---------------------
Msrc/backenddb/lookup_deposits_by_contract_and_coin.c | 96++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/backenddb/lookup_deposits_by_order.c | 38+++++++++++++++++---------------------
Msrc/backenddb/lookup_expected_transfer.c | 49++++++++++++++++++++++++-------------------------
Msrc/backenddb/lookup_expected_transfers.c | 154+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/backenddb/lookup_instances.c | 193+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/backenddb/lookup_inventory_products.c | 123+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/backenddb/lookup_inventory_products_filtered.c | 147+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/backenddb/lookup_login_tokens.c | 74++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/backenddb/lookup_mfa_challenge.c | 39++++++++++++++++++++-------------------
Msrc/backenddb/lookup_order.c | 28++++++++++++++--------------
Msrc/backenddb/lookup_order_by_fulfillment.c | 50+++++++++++++++++++++++++-------------------------
Msrc/backenddb/lookup_order_charity.c | 40++++++++++++++++++++--------------------
Msrc/backenddb/lookup_order_status.c | 26+++++++++++++-------------
Msrc/backenddb/lookup_order_status_by_serial.c | 28++++++++++++++--------------
Msrc/backenddb/lookup_order_summary.c | 42+++++++++++++++++++++++-------------------
Msrc/backenddb/lookup_orders.c | 261++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/backenddb/lookup_otp_devices.c | 23+++++++++++------------
Msrc/backenddb/lookup_pending_deposits.c | 60+++++++++++++++++++++++++++++++++++++++---------------------
Msrc/backenddb/lookup_pending_webhooks.c | 91++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/backenddb/lookup_product.c | 83+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/backenddb/lookup_product_image.c | 15+++++++--------
Msrc/backenddb/lookup_products.c | 110++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/backenddb/lookup_reconciliation_details.c | 47+++++++++++++++++++++++------------------------
Msrc/backenddb/lookup_refund_proof.c | 26+++++++++++---------------
Msrc/backenddb/lookup_refunds.c | 32++++++++++++++++----------------
Msrc/backenddb/lookup_refunds_detailed.c | 56++++++++++++++++++++++++++++----------------------------
Msrc/backenddb/lookup_reports_pending.c | 50+++++++++++++++++++++++++++-----------------------
Msrc/backenddb/lookup_spent_tokens_by_order.c | 36++++++++++++++++--------------------
Msrc/backenddb/lookup_statistics_amount_by_bucket.c | 39+++++++++++++++++++--------------------
Msrc/backenddb/lookup_statistics_amount_by_bucket2.c | 43+++++++++++++++++++++----------------------
Msrc/backenddb/lookup_statistics_amount_by_interval.c | 30++++++++++++------------------
Msrc/backenddb/lookup_statistics_counter_by_bucket.c | 37++++++++++++++++++-------------------
Msrc/backenddb/lookup_statistics_counter_by_bucket2.c | 41++++++++++++++++++++---------------------
Msrc/backenddb/lookup_statistics_counter_by_interval.c | 30++++++++++++------------------
Msrc/backenddb/lookup_template.c | 35+++++++++++++++++------------------
Msrc/backenddb/lookup_templates.c | 23+++++++++++------------
Msrc/backenddb/lookup_token_families.c | 33++++++++++++++++-----------------
Msrc/backenddb/lookup_token_family.c | 54+++++++++++++++++++++++++++---------------------------
Msrc/backenddb/lookup_token_family_key.c | 75+++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/backenddb/lookup_token_family_keys.c | 67+++++++++++++++++++++++++++++++++----------------------------------
Msrc/backenddb/lookup_transfer_details.c | 44++++++++++++++++++++------------------------
Msrc/backenddb/lookup_transfer_details_by_order.c | 55++++++++++++++++++++++++++-----------------------------
Msrc/backenddb/lookup_transfer_summary.c | 40++++++++++++++++++----------------------
Msrc/backenddb/lookup_transfers.c | 142+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/backenddb/lookup_units.c | 70++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/backenddb/lookup_webhook.c | 33++++++++++++++++-----------------
Msrc/backenddb/lookup_webhook_by_event.c | 33++++++++++++++++-----------------
Msrc/backenddb/lookup_webhooks.c | 23+++++++++++------------
Msrc/backenddb/mark_contract_paid.c | 98++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/backenddb/mark_order_wired.c | 16++++++----------
Msrc/backenddb/meson.build | 1-
Msrc/backenddb/pg.c | 1-
Dsrc/backenddb/pg_account_kyc_get_outdated.sql | 57---------------------------------------------------------
Dsrc/backenddb/pg_create_instance_schema.sql | 39---------------------------------------
Dsrc/backenddb/pg_create_instance_schema_procedures.sql.fragment | 2214-------------------------------------------------------------------------------
Dsrc/backenddb/pg_create_instance_schema_tables.sql.fragment | 961-------------------------------------------------------------------------------
Dsrc/backenddb/pg_create_instance_schema_triggers.sql.fragment | 736-------------------------------------------------------------------------------
Dsrc/backenddb/pg_create_instance_trigger.sql | 57---------------------------------------------------------
Dsrc/backenddb/pg_expire_locks.sql | 54------------------------------------------------------
Dsrc/backenddb/pg_lookup_instances.sql | 95-------------------------------------------------------------------------------
Dsrc/backenddb/pg_lookup_pending_deposits.sql | 230-------------------------------------------------------------------------------
Dsrc/backenddb/pg_lookup_pending_webhooks.sql | 133-------------------------------------------------------------------------------
Dsrc/backenddb/pg_select_accounts.sql | 84-------------------------------------------------------------------------------
Dsrc/backenddb/pg_select_all_donau_instances.sql | 116-------------------------------------------------------------------------------
Dsrc/backenddb/pg_select_open_transfers.sql | 124-------------------------------------------------------------------------------
Msrc/backenddb/purge_instance.c | 17-----------------
Msrc/backenddb/refund_coin.c | 63+++++++++++++++++++++++++++++++--------------------------------
Msrc/backenddb/select_account.c | 34+++++++++++++++++-----------------
Msrc/backenddb/select_account_by_uri.c | 32++++++++++++++++----------------
Msrc/backenddb/select_accounts.c | 48+++++++++++++++++++++++++++++-------------------
Msrc/backenddb/select_all_donau_instances.c | 42+++++++++++++++++++++++-------------------
Msrc/backenddb/select_category.c | 52++++++++++++++++++++++++++--------------------------
Msrc/backenddb/select_category_by_name.c | 25++++++++++++-------------
Msrc/backenddb/select_donau_instance_by_serial.c | 18+++++++-----------
Msrc/backenddb/select_donau_instances.c | 44++++++++++++++++++++------------------------
Msrc/backenddb/select_donau_instances_filtered.c | 7++++---
Msrc/backenddb/select_login_token.c | 26+++++++++++++-------------
Msrc/backenddb/select_money_pot.c | 27+++++++++++++--------------
Msrc/backenddb/select_money_pots.c | 58+++++++++++++++++++++++++++++-----------------------------
Msrc/backenddb/select_open_transfers.c | 33++++++++++++++++++++-------------
Msrc/backenddb/select_order_blinded_sigs.c | 24++++++++++--------------
Msrc/backenddb/select_otp.c | 34+++++++++++++++++-----------------
Msrc/backenddb/select_otp_serial.c | 23+++++++++++------------
Msrc/backenddb/select_product_groups.c | 58+++++++++++++++++++++++++++++-----------------------------
Msrc/backenddb/select_report.c | 41++++++++++++++++++++---------------------
Msrc/backenddb/select_reports.c | 62+++++++++++++++++++++++++++++++-------------------------------
Msrc/backenddb/select_unit.c | 103+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/backenddb/select_wirewatch_accounts.c | 26+++++++++++++++-----------
Dsrc/backenddb/set_instance.c | 82-------------------------------------------------------------------------------
Msrc/backenddb/solve_mfa_challenge.c | 19++++++++-----------
Msrc/backenddb/sql-schema/gen-procedures.sh | 48++++++++++--------------------------------------
Dsrc/backenddb/sql-schema/merchant-0036-copy.sql.fragment | 504-------------------------------------------------------------------------------
Dsrc/backenddb/sql-schema/merchant-0036-drop.sql.fragment | 50--------------------------------------------------
Dsrc/backenddb/sql-schema/merchant-0036-setval.sql.fragment | 35-----------------------------------
Dsrc/backenddb/sql-schema/merchant-0036.sql.in | 88-------------------------------------------------------------------------------
Msrc/backenddb/sql-schema/meson.build | 1-
Msrc/backenddb/unlock_inventory.c | 16++++++----------
Msrc/backenddb/update_account.c | 28++++++++++++++--------------
Msrc/backenddb/update_category.c | 24++++++++++++------------
Msrc/backenddb/update_contract_session.c | 34+++++++++++++++++-----------------
Msrc/backenddb/update_contract_terms.c | 31+++++++++++++++----------------
Msrc/backenddb/update_deposit_confirmation_status.c | 24++++++++++--------------
Msrc/backenddb/update_donau_instance.c | 28+++++++++++++++-------------
Msrc/backenddb/update_donau_instance_receipts_amount.c | 16++++++----------
Msrc/backenddb/update_mfa_challenge.c | 23++++++++++-------------
Msrc/backenddb/update_money_pot.c | 24++++++++++--------------
Msrc/backenddb/update_otp.c | 28++++++++++++++--------------
Msrc/backenddb/update_pending_webhook.c | 18+++++++-----------
Msrc/backenddb/update_product.c | 64+++++++++++++++++++++++++++++++---------------------------------
Msrc/backenddb/update_product_group.c | 22+++++++++-------------
Msrc/backenddb/update_report.c | 36++++++++++++++++++------------------
Msrc/backenddb/update_report_status.c | 26+++++++++++++-------------
Msrc/backenddb/update_template.c | 44++++++++++++++++++++++++--------------------
Msrc/backenddb/update_token_family.c | 32++++++++++++++++----------------
Msrc/backenddb/update_transfer_status.c | 26+++++++++++---------------
Msrc/backenddb/update_unit.c | 26++++++++++++--------------
Msrc/backenddb/update_webhook.c | 30+++++++++++++++---------------
Msrc/backenddb/update_wirewatch_progress.c | 24++++++++++++------------
Dsrc/include/merchant-database/set_instance.h | 51---------------------------------------------------
193 files changed, 3639 insertions(+), 9494 deletions(-)

diff --git a/src/backenddb/account_kyc_get_outdated.c b/src/backenddb/account_kyc_get_outdated.c @@ -76,11 +76,11 @@ kyc_status_cb (void *cls, char *exchange_url; char *instance_id; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("out_h_wire", + GNUNET_PQ_result_spec_auto_from_type ("h_wire", &h_wire), - GNUNET_PQ_result_spec_string ("out_exchange_url", + GNUNET_PQ_result_spec_string ("exchange_url", &exchange_url), - GNUNET_PQ_result_spec_string ("out_merchant_id", + GNUNET_PQ_result_spec_string ("instance_id", &instance_id), GNUNET_PQ_result_spec_end }; @@ -123,11 +123,17 @@ TALER_MERCHANTDB_account_kyc_get_outdated (struct TALER_MERCHANTDB_PostgresConte check_connection (pg); PREPARE (pg, "account_kyc_get_outdated", - "SELECT" - " out_merchant_id" - " ,out_h_wire" - " ,out_exchange_url" - " FROM merchant.account_kyc_get_outdated($1)"); + "SELECT " + " mi.merchant_id" + " ,ma.h_wire" + " ,kyc.exchange_url" + " FROM merchant_kyc kyc" + " JOIN merchant_accounts ma" + " USING (account_serial)" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE kyc.next_kyc_poll < $1" + " ORDER BY kyc.next_kyc_poll ASC;"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, "account_kyc_get_outdated", diff --git a/src/backenddb/account_kyc_get_status.c b/src/backenddb/account_kyc_get_status.c @@ -172,6 +172,7 @@ TALER_MERCHANTDB_account_kyc_get_status ( struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (merchant_id), GNUNET_PQ_query_param_absolute_time (&now), NULL == exchange_url ? GNUNET_PQ_query_param_null () @@ -182,30 +183,25 @@ TALER_MERCHANTDB_account_kyc_get_status ( GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (merchant_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "account_kyc_get_status", - "SELECT " - " out_h_wire AS h_wire" - " ,out_payto_uri AS payto_uri" - " ,out_exchange_url AS exchange_url" - " ,out_kyc_timestamp AS kyc_timestamp" - " ,out_kyc_ok AS kyc_ok" - " ,out_access_token AS access_token" - " ,out_exchange_http_status AS exchange_http_status" - " ,out_exchange_ec_code AS exchange_ec_code" - " ,out_aml_review AS aml_review" - " ,out_jaccount_limits::TEXT AS jaccount_limits" - " FROM merchant_do_account_kyc_get_status($1, $2, $3);"); + PREPARE (pg, + "account_kyc_get_status", + "SELECT " + " out_h_wire AS h_wire" + " ,out_payto_uri AS payto_uri" + " ,out_exchange_url AS exchange_url" + " ,out_kyc_timestamp AS kyc_timestamp" + " ,out_kyc_ok AS kyc_ok" + " ,out_access_token AS access_token" + " ,out_exchange_http_status AS exchange_http_status" + " ,out_exchange_ec_code AS exchange_ec_code" + " ,out_aml_review AS aml_review" + " ,out_jaccount_limits::TEXT AS jaccount_limits" + " FROM merchant_do_account_kyc_get_status($1, $2, $3, $4);"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "account_kyc_get_status", params, &kyc_status_cb, &ksc); diff --git a/src/backenddb/account_kyc_set_failed.c b/src/backenddb/account_kyc_set_failed.c @@ -50,6 +50,7 @@ TALER_MERCHANTDB_account_kyc_set_failed (struct TALER_MERCHANTDB_PostgresContext = GNUNET_PQ_get_event_notify_channel (&hdr); uint32_t http_status32 = (uint32_t) exchange_http_status; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (merchant_id), GNUNET_PQ_query_param_auto_from_type (h_wire), GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_timestamp (&timestamp), @@ -59,29 +60,28 @@ TALER_MERCHANTDB_account_kyc_set_failed (struct TALER_MERCHANTDB_PostgresContext GNUNET_PQ_query_param_string (notify2_s), GNUNET_PQ_query_param_end }; + bool no_instance; bool no_account; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("no_instance", + &no_instance), GNUNET_PQ_result_spec_bool ("no_account", &no_account), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (merchant_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "account_kyc_set_failed", - "SELECT " - " out_no_account AS no_account" - " FROM merchant_do_account_kyc_set_failed" - "($1, $2, $3, $4, $5, $6, $7);"); + PREPARE (pg, + "account_kyc_set_failed", + "SELECT " + " out_no_instance AS no_instance" + " ,out_no_account AS no_account" + " FROM merchant_do_account_kyc_set_failed" + "($1, $2, $3, $4, $5, $6, $7, $8);"); qs = GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "account_kyc_set_failed", params, rs); GNUNET_free (notify_s); @@ -93,6 +93,7 @@ TALER_MERCHANTDB_account_kyc_set_failed (struct TALER_MERCHANTDB_PostgresContext GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); return qs; } + GNUNET_break (! no_instance); GNUNET_break (! no_account); return qs; } diff --git a/src/backenddb/account_kyc_set_status.c b/src/backenddb/account_kyc_set_status.c @@ -59,6 +59,7 @@ TALER_MERCHANTDB_account_kyc_set_status (struct TALER_MERCHANTDB_PostgresContext uint32_t http_status32 = (uint32_t) exchange_http_status; uint32_t ec_code32 = (uint32_t) exchange_ec_code; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (merchant_id), GNUNET_PQ_query_param_auto_from_type (h_wire), GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_timestamp (&timestamp), @@ -79,30 +80,29 @@ TALER_MERCHANTDB_account_kyc_set_status (struct TALER_MERCHANTDB_PostgresContext GNUNET_PQ_query_param_relative_time (&kyc_backoff), GNUNET_PQ_query_param_end }; + bool no_instance; bool no_account; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("no_instance", + &no_instance), GNUNET_PQ_result_spec_bool ("no_account", &no_account), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (merchant_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "account_kyc_set_status", - "SELECT " - " out_no_account AS no_account" - " FROM merchant_do_account_kyc_set_status" - "($1, $2, $3, $4, $5, $6, $7::TEXT::JSONB" - ",$8, $9, $10, $11, $12, $13, $14);"); + PREPARE (pg, + "account_kyc_set_status", + "SELECT " + " out_no_instance AS no_instance" + " ,out_no_account AS no_account" + " FROM merchant_do_account_kyc_set_status" + "($1, $2, $3, $4, $5, $6, $7, $8::TEXT::JSONB" + ",$9, $10, $11, $12, $13, $14, $15);"); qs = GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "account_kyc_set_status", params, rs); GNUNET_free (notify_s); @@ -114,6 +114,7 @@ TALER_MERCHANTDB_account_kyc_set_status (struct TALER_MERCHANTDB_PostgresContext GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); return qs; } + GNUNET_break (! no_instance); GNUNET_break (! no_account); return qs; } diff --git a/src/backenddb/activate_account.c b/src/backenddb/activate_account.c @@ -35,6 +35,7 @@ TALER_MERCHANTDB_activate_account (struct TALER_MERCHANTDB_PostgresContext *pg, bool *conflict) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (account_details->instance_id), GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire), GNUNET_PQ_query_param_auto_from_type (&account_details->salt), GNUNET_PQ_query_param_string (account_details->payto_uri.full_payto), @@ -61,25 +62,20 @@ TALER_MERCHANTDB_activate_account (struct TALER_MERCHANTDB_PostgresContext *pg, salt), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; GNUNET_assert (account_details->active); - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (account_details->instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "activate_account", - "SELECT " - " out_h_wire AS h_wire" - " ,out_salt AS salt" - " ,out_conflict AS conflict" - " ,out_not_found AS not_found" - " FROM merchant_do_activate_account" - " ($1,$2,$3,$4,$5,$6);"); + PREPARE (pg, + "activate_account", + "SELECT " + " out_h_wire AS h_wire" + " ,out_salt AS salt" + " ,out_conflict AS conflict" + " ,out_not_found AS not_found" + " FROM merchant_do_activate_account" + " ($1,$2,$3,$4,$5,$6,$7);"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "activate_account", params, rs); } diff --git a/src/backenddb/check_donau_instance.c b/src/backenddb/check_donau_instance.c @@ -36,25 +36,26 @@ TALER_MERCHANTDB_check_donau_instance (struct TALER_MERCHANTDB_PostgresContext * struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (donau_url), GNUNET_PQ_query_param_uint64 (&charity_id), + GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - (void) merchant_pub; - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "check_donau_instance", - "SELECT 1" - " FROM merchant_donau_instances" - " WHERE donau_url=$1" - " AND charity_id=$2"); + PREPARE (pg, + "check_donau_instance", + "SELECT 1" + " FROM merchant_donau_instances" + " WHERE donau_url=$1" + " AND charity_id=$2 " + " AND merchant_instance_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances mi" + " WHERE mi.merchant_pub = $3)"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "check_donau_instance", params, rs); diff --git a/src/backenddb/check_money_pots.c b/src/backenddb/check_money_pots.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_check_money_pots (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t *pot_missing) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_array_uint64 (pots_len, pots, pg->conn), @@ -45,26 +46,24 @@ TALER_MERCHANTDB_check_money_pots (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "check_money_pots", - "SELECT n AS out_missing" - " FROM UNNEST($1::INT8[]) AS n" - " WHERE NOT EXISTS (" - " SELECT 1" - " FROM merchant_money_pots mmp" - " WHERE mmp.money_pot_serial=n" - " )" - " LIMIT 1;"); + PREPARE (pg, + "check_money_pots", + "SELECT n AS out_missing" + " FROM UNNEST($2::INT8[]) AS n" + " WHERE NOT EXISTS (" + " SELECT 1" + " FROM merchant_money_pots mmp" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mmp.money_pot_serial=n" + " AND mi.merchant_id=$1" + " )" + " LIMIT 1;"); qs = GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "check_money_pots", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/check_report.c b/src/backenddb/check_report.c @@ -40,9 +40,9 @@ TALER_MERCHANTDB_check_report (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("out_merchant_id", + GNUNET_PQ_result_spec_string ("merchant_id", instance_id), - GNUNET_PQ_result_spec_string ("out_data_source", + GNUNET_PQ_result_spec_string ("data_source", data_source), GNUNET_PQ_result_spec_end }; @@ -51,9 +51,14 @@ TALER_MERCHANTDB_check_report (struct TALER_MERCHANTDB_PostgresContext *pg, PREPARE (pg, "check_report", "SELECT" - " out_merchant_id" - " ,out_data_source" - " FROM merchant.check_report($1, $2, $3)"); + " mi.merchant_id" + " ,mr.data_source" + " FROM merchant_reports mr" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mr.report_serial=$1" + " AND mr.report_token=$2" + " AND mr.mime_type=$3;"); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, "check_report", diff --git a/src/backenddb/check_transfer_exists.c b/src/backenddb/check_transfer_exists.c @@ -31,28 +31,32 @@ TALER_MERCHANTDB_check_transfer_exists (struct TALER_MERCHANTDB_PostgresContext uint64_t transfer_serial_id) { 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 }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "check_transfer_exists", - "SELECT" - " 1" - " FROM merchant_transfers" - " WHERE credit_serial=$1"); + PREPARE (pg, + "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)"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "check_transfer_exists", params, rs); } diff --git a/src/backenddb/create_mfa_challenge.c b/src/backenddb/create_mfa_challenge.c @@ -55,6 +55,7 @@ TALER_MERCHANTDB_create_mfa_challenge ( GNUNET_PQ_query_param_absolute_time (&retransmission_date), GNUNET_PQ_query_param_string (channel_str), GNUNET_PQ_query_param_string (required_address), /* $9 */ + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -62,30 +63,28 @@ TALER_MERCHANTDB_create_mfa_challenge ( challenge_id), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); - PREPARE_INSTANCE (pg, - stmt, - "create_mfa_challenge", - "INSERT INTO tan_challenges" - " (h_body" - " ,salt" - " ,op" - " ,code" - " ,creation_date" - " ,expiration_date" - " ,retransmission_date" - " ,retry_counter" /* always set to 3 */ - " ,tan_channel" - " ,required_address)" - " VALUES" - " ($1, $2, $3, $4, $5, $6, $7, 3, $8, $9)" - " RETURNING challenge_id;"); + PREPARE (pg, + "create_mfa_challenge", + "INSERT INTO tan_challenges" + " (h_body" + " ,salt" + " ,op" + " ,code" + " ,creation_date" + " ,expiration_date" + " ,retransmission_date" + " ,retry_counter" /* always set to 3 */ + " ,tan_channel" + " ,required_address" + " ,merchant_serial)" + " SELECT" + " $1, $2, $3, $4, $5, $6, $7, 3, $8, $9, merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$10" + " RETURNING challenge_id;"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "create_mfa_challenge", params, rs); } diff --git a/src/backenddb/delete_category.c b/src/backenddb/delete_category.c @@ -32,22 +32,22 @@ TALER_MERCHANTDB_delete_category (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t category_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&category_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_category", - "DELETE" - " FROM merchant_categories" - " WHERE category_serial=$1"); + PREPARE (pg, + "delete_category", + "DELETE" + " FROM merchant_categories" + " WHERE merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND category_serial=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_category", params); } diff --git a/src/backenddb/delete_contract_terms.c b/src/backenddb/delete_contract_terms.c @@ -33,26 +33,26 @@ TALER_MERCHANTDB_delete_contract_terms (struct TALER_MERCHANTDB_PostgresContext { 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_string (order_id), GNUNET_PQ_query_param_relative_time (&legal_expiration), GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_contract_terms", - "DELETE FROM merchant_contract_terms" - " WHERE order_id=$1" - " AND ( ( (pay_deadline < $3) AND" - " (NOT paid) ) OR" - " (creation_time + $2 < $3) )"); + PREPARE (pg, + "delete_contract_terms", + "DELETE FROM merchant_contract_terms" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND ( ( (pay_deadline < $4) AND" + " (NOT paid) ) OR" + " (creation_time + $3 < $4) )"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_contract_terms", params); } diff --git a/src/backenddb/delete_donau_instance.c b/src/backenddb/delete_donau_instance.c @@ -33,20 +33,19 @@ TALER_MERCHANTDB_delete_donau_instance (struct TALER_MERCHANTDB_PostgresContext { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&donau_serial_id), + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_donau_instance", - "DELETE FROM merchant_donau_instances" - " WHERE donau_instances_serial = $1;"); + PREPARE (pg, + "delete_donau_instance", + "DELETE FROM merchant_donau_instances di" + " USING merchant_instances mi" + " WHERE di.merchant_instance_serial = mi.merchant_serial" + " AND di.donau_instances_serial = $1" + " AND mi.merchant_id = $2;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_donau_instance", params); } \ No newline at end of file diff --git a/src/backenddb/delete_instance_private_key.c b/src/backenddb/delete_instance_private_key.c @@ -30,19 +30,19 @@ TALER_MERCHANTDB_delete_instance_private_key (struct TALER_MERCHANTDB_PostgresCo const char *merchant_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (merchant_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (merchant_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_key", - "DELETE FROM merchant_keys"); + PREPARE (pg, + "delete_key", + "DELETE FROM merchant_keys" + " USING merchant_instances" + " WHERE merchant_keys.merchant_serial" + " = merchant_instances.merchant_serial" + " AND merchant_instances.merchant_id = $1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_key", params); } diff --git a/src/backenddb/delete_login_token.c b/src/backenddb/delete_login_token.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2023, 2026 Taler Systems SA + Copyright (C) 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -31,22 +31,22 @@ TALER_MERCHANTDB_delete_login_token_serial (struct TALER_MERCHANTDB_PostgresCont uint64_t serial) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_uint64 (&serial), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_login_token_serial", - "DELETE FROM merchant_login_tokens" - " WHERE serial=$1"); + PREPARE (pg, + "delete_login_token_serial", + "DELETE FROM merchant_login_tokens" + " WHERE serial=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_login_token_serial", params); } @@ -57,21 +57,21 @@ TALER_MERCHANTDB_delete_login_token (struct TALER_MERCHANTDB_PostgresContext *pg const struct TALER_MERCHANTDB_LoginTokenP *token) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_auto_from_type (token), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_login_token", - "DELETE FROM merchant_login_tokens" - " WHERE token=$1"); + PREPARE (pg, + "delete_login_token", + "DELETE FROM merchant_login_tokens" + " WHERE token=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_login_token", params); } diff --git a/src/backenddb/delete_money_pot.c b/src/backenddb/delete_money_pot.c @@ -32,22 +32,22 @@ TALER_MERCHANTDB_delete_money_pot (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t money_pot_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&money_pot_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_money_pot", - "DELETE" - " FROM merchant_money_pots" - " WHERE money_pot_serial=$1"); + PREPARE (pg, + "delete_money_pot", + "DELETE" + " FROM merchant_money_pots" + " WHERE merchant_money_pots.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_money_pots.money_pot_serial=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_money_pot", params); } diff --git a/src/backenddb/delete_order.c b/src/backenddb/delete_order.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022, 2026 Taler Systems SA + Copyright (C) 2022 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -33,51 +33,56 @@ TALER_MERCHANTDB_delete_order (struct TALER_MERCHANTDB_PostgresContext *pg, { 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_string (order_id), GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_bool (force), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_QueryParam params2[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs2; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - char stmt2[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_order", - "WITH mc AS" - "(SELECT paid" - " FROM merchant_contract_terms" - " WHERE order_id=$1) " - "DELETE" - " FROM merchant_orders mo" - " WHERE order_id=$1" - " AND ( (pay_deadline < $2)" - " OR (NOT EXISTS (SELECT paid FROM mc))" - " OR ($3 AND (FALSE=(SELECT paid FROM mc))) );"); + PREPARE (pg, + "delete_order", + "WITH ms AS" + "(SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + ", mc AS" + "(SELECT paid" + " FROM merchant_contract_terms" + " JOIN ms USING (merchant_serial)" + " WHERE order_id=$2) " + "DELETE" + " FROM merchant_orders mo" + " WHERE order_id=$2" + " AND merchant_serial=(SELECT merchant_serial FROM ms)" + " AND ( (pay_deadline < $3)" + " OR (NOT EXISTS (SELECT paid FROM mc))" + " OR ($4 AND (FALSE=(SELECT paid FROM mc))) );"); qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_order", params); if ( (qs < 0) || (! force) ) return qs; - PREPARE_INSTANCE (pg, - stmt2, - "delete_contract", - "DELETE" - " FROM merchant_contract_terms" - " WHERE order_id=$1" - " AND NOT paid;"); + PREPARE (pg, + "delete_contract", + "DELETE" + " FROM merchant_contract_terms" + " WHERE order_id=$2 AND" + " merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND NOT paid;"); qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt2, + "delete_contract", params2); if (qs2 < 0) return qs2; diff --git a/src/backenddb/delete_otp.c b/src/backenddb/delete_otp.c @@ -32,22 +32,22 @@ TALER_MERCHANTDB_delete_otp (struct TALER_MERCHANTDB_PostgresContext *pg, const char *otp_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (otp_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_otp", - "DELETE" - " FROM merchant_otp_devices" - " WHERE otp_id=$1"); + PREPARE (pg, + "delete_otp", + "DELETE" + " FROM merchant_otp_devices" + " WHERE merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND otp_id=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_otp", params); } diff --git a/src/backenddb/delete_pending_webhook.c b/src/backenddb/delete_pending_webhook.c @@ -34,18 +34,14 @@ TALER_MERCHANTDB_delete_pending_webhook (struct TALER_MERCHANTDB_PostgresContext GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_pending_webhook", - "DELETE" - " FROM merchant_pending_webhooks" - " WHERE webhook_pending_serial=$1"); + PREPARE (pg, + "delete_pending_webhook", + "DELETE" + " FROM merchant_pending_webhooks" + " WHERE webhook_pending_serial=$1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_pending_webhook", params); } diff --git a/src/backenddb/delete_product.c b/src/backenddb/delete_product.c @@ -31,26 +31,26 @@ TALER_MERCHANTDB_delete_product (struct TALER_MERCHANTDB_PostgresContext *pg, const char *product_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (product_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_product", - "DELETE" - " FROM merchant_inventory" - " WHERE product_id=$1" - " AND product_serial NOT IN " - " (SELECT product_serial FROM merchant_order_locks)" - " AND product_serial NOT IN " - " (SELECT product_serial FROM merchant_inventory_locks)"); + PREPARE (pg, + "delete_product", + "DELETE" + " FROM merchant_inventory" + " WHERE merchant_inventory.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_inventory.product_id=$2" + " AND product_serial NOT IN " + " (SELECT product_serial FROM merchant_order_locks)" + " AND product_serial NOT IN " + " (SELECT product_serial FROM merchant_inventory_locks)"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_product", params); } diff --git a/src/backenddb/delete_product_group.c b/src/backenddb/delete_product_group.c @@ -31,22 +31,22 @@ TALER_MERCHANTDB_delete_product_group (struct TALER_MERCHANTDB_PostgresContext * uint64_t product_group_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&product_group_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_product_group", - "DELETE" - " FROM merchant_product_groups" - " WHERE product_group_serial=$1"); + PREPARE (pg, + "delete_product_group", + "DELETE" + " FROM merchant_product_groups" + " WHERE merchant_product_groups.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_product_groups.product_group_serial=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_product_group", params); } diff --git a/src/backenddb/delete_report.c b/src/backenddb/delete_report.c @@ -32,22 +32,22 @@ TALER_MERCHANTDB_delete_report (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t report_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&report_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_report", - "DELETE" - " FROM merchant_reports" - " WHERE report_serial=$1"); + PREPARE (pg, + "delete_report", + "DELETE" + " FROM merchant_reports" + " WHERE merchant_reports.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_reports.report_serial=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_report", params); } diff --git a/src/backenddb/delete_template.c b/src/backenddb/delete_template.c @@ -32,22 +32,22 @@ TALER_MERCHANTDB_delete_template (struct TALER_MERCHANTDB_PostgresContext *pg, const char *template_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (template_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_template", - "DELETE" - " FROM merchant_template" - " WHERE template_id=$1"); + PREPARE (pg, + "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"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_template", params); } diff --git a/src/backenddb/delete_token_family.c b/src/backenddb/delete_token_family.c @@ -31,22 +31,22 @@ TALER_MERCHANTDB_delete_token_family (struct TALER_MERCHANTDB_PostgresContext *p const char *token_family_slug) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (token_family_slug), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_token_family", - "DELETE" - " FROM merchant_token_families" - " WHERE slug=$1"); + PREPARE (pg, + "delete_token_family", + "DELETE" + " FROM merchant_token_families" + " WHERE merchant_token_families.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND slug=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_token_family", params); } \ No newline at end of file diff --git a/src/backenddb/delete_transfer.c b/src/backenddb/delete_transfer.c @@ -32,22 +32,26 @@ TALER_MERCHANTDB_delete_transfer (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t transfer_serial_id) { 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 }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_transfer", - "DELETE FROM merchant_transfers" - " WHERE credit_serial=$1"); + PREPARE (pg, + "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))"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_transfer", params); } diff --git a/src/backenddb/delete_unit.c b/src/backenddb/delete_unit.c @@ -35,10 +35,13 @@ TALER_MERCHANTDB_delete_unit (struct TALER_MERCHANTDB_PostgresContext *pg, bool *builtin_conflict) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (unit_id), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("out_no_instance", + no_instance), GNUNET_PQ_result_spec_bool ("out_no_unit", no_unit), GNUNET_PQ_result_spec_bool ("out_builtin_conflict", @@ -46,22 +49,17 @@ TALER_MERCHANTDB_delete_unit (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - *no_instance = false; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_unit", - "SELECT" - " out_no_unit" - " ,out_builtin_conflict" - " FROM merchant_do_delete_unit($1);"); + PREPARE (pg, + "delete_unit", + "SELECT" + " out_no_instance" + " ,out_no_unit" + " ,out_builtin_conflict" + " FROM merchant_do_delete_unit($1,$2);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "delete_unit", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/delete_webhook.c b/src/backenddb/delete_webhook.c @@ -31,23 +31,23 @@ TALER_MERCHANTDB_delete_webhook (struct TALER_MERCHANTDB_PostgresContext *pg, const char *webhook_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (webhook_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "delete_webhook", - "DELETE" - " FROM merchant_webhook" - " WHERE webhook_id=$1"); + PREPARE (pg, + "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"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "delete_webhook", params); } diff --git a/src/backenddb/expire_locks.c b/src/backenddb/expire_locks.c @@ -34,29 +34,52 @@ TALER_MERCHANTDB_expire_locks (struct TALER_MERCHANTDB_PostgresContext *pg) GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_end }; - uint64_t total; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("expire_locks", - &total), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; + enum GNUNET_DB_QueryStatus qs1; + enum GNUNET_DB_QueryStatus qs2; + enum GNUNET_DB_QueryStatus qs3; check_connection (pg); PREPARE (pg, - "expire_locks", - "SELECT merchant.expire_locks($1) AS expire_locks"); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "expire_locks", - params, - rs); - if (qs < 0) + "unlock_products", + "DELETE FROM merchant_inventory_locks" + " WHERE expiration < $1"); + qs1 = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "unlock_products", + params); + if (qs1 < 0) + { + GNUNET_break (0); + return qs1; + } + PREPARE (pg, + "unlock_orders", + "DELETE FROM merchant_orders" + " WHERE pay_deadline < $1"); + qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "unlock_orders", + params); + if (qs2 < 0) + { + GNUNET_break (0); + return qs2; + } + PREPARE (pg, + "unlock_contracts", + "DELETE FROM merchant_contract_terms" + " WHERE NOT paid" + " AND pay_deadline < $1"); + qs3 = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "unlock_contracts", + params); + if (qs3 < 0) { GNUNET_break (0); - return qs; + return qs3; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Released %llu locks across all instances\n", - (unsigned long long) total); - return (enum GNUNET_DB_QueryStatus) total; + "Released %d+%d+%d locks\n", + qs1, + qs2, + qs3); + return qs1 + qs2 + qs3; } diff --git a/src/backenddb/finalize_transfer_status.c b/src/backenddb/finalize_transfer_status.c @@ -49,34 +49,30 @@ TALER_MERCHANTDB_finalize_transfer_status (struct TALER_MERCHANTDB_PostgresConte GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "finalize_transfer_status", - "WITH subquery AS (" - " SELECT signkey_serial" - " FROM merchant.merchant_exchange_signing_keys" - " WHERE exchange_pub=$6" - ")" - "UPDATE merchant_expected_transfers SET" - " last_http_status=200" - ",last_ec=0" - ",last_detail=NULL" - ",retry_needed=FALSE" - ",retry_time=0" - ",expected_credit_amount=$3" - ",wire_fee=$4" - ",h_details=$5" - ",signkey_serial=subquery.signkey_serial" - ",exchange_sig=$7" - " FROM subquery" - " WHERE wtid=$1" - " AND exchange_url=$2"); + PREPARE (pg, + "finalize_transfer_status", + "WITH subquery AS (" + " SELECT signkey_serial" + " FROM merchant_exchange_signing_keys" + " WHERE exchange_pub=$6" + ")" + "UPDATE merchant_expected_transfers SET" + " last_http_status=200" + ",last_ec=0" + ",last_detail=NULL" + ",retry_needed=FALSE" + ",retry_time=0" + ",expected_credit_amount=$3" + ",wire_fee=$4" + ",h_details=$5" + ",signkey_serial=subquery.signkey_serial" + ",exchange_sig=$7" + " FROM subquery" + " WHERE wtid=$1" + " AND exchange_url=$2"); return GNUNET_PQ_eval_prepared_non_select ( pg->conn, - stmt, + "finalize_transfer_status", params); } diff --git a/src/backenddb/get_kyc_limits.c b/src/backenddb/get_kyc_limits.c @@ -36,6 +36,7 @@ TALER_MERCHANTDB_get_kyc_limits (struct TALER_MERCHANTDB_PostgresContext *pg, { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (merchant_account_uri.full_payto), + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_end }; @@ -50,28 +51,27 @@ TALER_MERCHANTDB_get_kyc_limits (struct TALER_MERCHANTDB_PostgresContext *pg, NULL), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "get_kyc_limits", - "SELECT" - " mk.kyc_ok" - ",mk.jaccount_limits::TEXT" - ",mk.access_token IS NULL AS no_access_token" - " FROM merchant_kyc mk" - " WHERE mk.exchange_url=$2" - " AND mk.account_serial=" - " (SELECT account_serial" - " FROM merchant_accounts" - " WHERE payto_uri=$1);"); + PREPARE (pg, + "get_kyc_limits", + "SELECT" + " mk.kyc_ok" + ",mk.jaccount_limits::TEXT" + ",mk.access_token IS NULL AS no_access_token" + " FROM merchant_kyc mk" + " WHERE mk.exchange_url=$3" + " AND mk.account_serial=" + " (SELECT account_serial" + " FROM merchant_accounts" + " WHERE payto_uri=$1" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$2));"); *jlimits = NULL; return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "get_kyc_limits", params, rs); } diff --git a/src/backenddb/get_kyc_status.c b/src/backenddb/get_kyc_status.c @@ -45,6 +45,7 @@ TALER_MERCHANTDB_get_kyc_status (struct TALER_MERCHANTDB_PostgresContext *pg, { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (merchant_account_uri.full_payto), + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_end }; @@ -79,35 +80,34 @@ TALER_MERCHANTDB_get_kyc_status (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "get_kyc_status", - "SELECT" - " mk.access_token" - ",mk.exchange_http_status" - ",mk.exchange_ec_code" - ",mk.kyc_ok" - ",mk.last_rule_gen" - ",mk.kyc_timestamp" - ",mk.next_kyc_poll" - ",mk.kyc_backoff" - ",mk.aml_review" - ",mk.jaccount_limits::TEXT" - " FROM merchant_kyc mk" - " WHERE mk.exchange_url=$2" - " AND mk.account_serial=" - " (SELECT account_serial" - " FROM merchant_accounts" - " WHERE payto_uri=$1);"); + PREPARE (pg, + "get_kyc_status", + "SELECT" + " mk.access_token" + ",mk.exchange_http_status" + ",mk.exchange_ec_code" + ",mk.kyc_ok" + ",mk.last_rule_gen" + ",mk.kyc_timestamp" + ",mk.next_kyc_poll" + ",mk.kyc_backoff" + ",mk.aml_review" + ",mk.jaccount_limits::TEXT" + " FROM merchant_kyc mk" + " WHERE mk.exchange_url=$3" + " AND mk.account_serial=" + " (SELECT account_serial" + " FROM merchant_accounts" + " WHERE payto_uri=$1" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$2));"); *jlimits = NULL; qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "get_kyc_status", params, rs); *last_ec = (enum TALER_ErrorCode) (int) e32; diff --git a/src/backenddb/helper.h b/src/backenddb/helper.h @@ -53,20 +53,6 @@ struct TALER_MERCHANTDB_PostgresContext const char *transaction_name; /** - * Instance id ("merchant_id") that the search_path is currently - * pointing at, or NULL if no per-instance schema is selected. - * Owned by this struct; set by TALER_MERCHANTDB_set_instance(). - */ - char *current_merchant_id; - - /** - * merchant_serial corresponding to @e current_merchant_id, or 0 - * if no per-instance schema is selected. Used as the suffix in - * per-instance prepared-statement names. - */ - uint64_t current_merchant_serial; - - /** * How many times have we connected to the DB. */ uint64_t prep_gen; @@ -107,94 +93,6 @@ struct TALER_MERCHANTDB_PostgresContext /** - * Maximum length (incl. NUL) of a per-instance prepared-statement name buffer. - * Generous: longest base name in this library is well under 56 chars and a - * 64-bit serial fits in 20 decimal digits + underscore + NUL. - */ -#define PG_PREP_INSTANCE_NAME_MAX 96 - - -/** - * Number of slots in each per-call-site (serial -> gen) cache used by - * #PREPARE_INSTANCE. A typical merchant deployment has a handful of - * instances; this is sized to comfortably cover that case. On a miss - * (cache thrash with more instances active than slots), the macro - * falls back to re-issuing PREPARE, which is correct as long as the - * statement was deallocated by the connection drop that bumped - * @e prep_gen. Increase if multi-tenant deployments thrash. - */ -#define PG_PREP_INSTANCE_CACHE_SLOTS 8 - - -/** - * Per-instance variant of #PREPARE. Prepares SQL statement @a sql - * under the connection-scoped name "<base>_<merchant_serial>" once - * per (call-site, merchant_serial, prep_gen). Writes the resolved - * name into @a sname (a caller-provided char buffer of at least - * #PG_PREP_INSTANCE_NAME_MAX bytes) so the caller can pass it to - * GNUNET_PQ_eval_prepared_*. - * - * Per-instance call sites must run with @e current_merchant_serial - * != 0 (i.e. after a successful TALER_MERCHANTDB_set_instance()). - * - * Returns with #GNUNET_DB_STATUS_HARD_ERROR on failure. - * - * @param pg a `struct TALER_MERCHANTDB_PostgresContext` - * @param sname caller-owned char buffer receiving the resolved name - * @param base base statement name (string literal) - * @param sql actual SQL text - */ -#define PREPARE_INSTANCE(pg,sname,base,sql) \ - do { \ - static struct { \ - uint64_t serial; \ - unsigned long long gen; \ - } _pi_cache[PG_PREP_INSTANCE_CACHE_SLOTS]; \ - static unsigned int _pi_next; \ - bool _pi_hit = false; \ - \ - GNUNET_assert (0 != (pg)->current_merchant_serial); \ - GNUNET_snprintf ((sname), \ - PG_PREP_INSTANCE_NAME_MAX, \ - "%s_%llu", \ - base, \ - (unsigned long long) (pg)-> \ - current_merchant_serial); \ - for (unsigned int _i = 0; \ - _i < PG_PREP_INSTANCE_CACHE_SLOTS; \ - _i++) \ - { \ - if ( (_pi_cache[_i].gen == (pg)->prep_gen) && \ - (_pi_cache[_i].serial == \ - (pg)->current_merchant_serial) ) \ - { \ - _pi_hit = true; \ - break; \ - } \ - } \ - if (! _pi_hit) \ - { \ - struct GNUNET_PQ_PreparedStatement _pi_ps[] = { \ - GNUNET_PQ_make_prepare ((sname), sql), \ - GNUNET_PQ_PREPARED_STATEMENT_END \ - }; \ - \ - if (GNUNET_OK != \ - GNUNET_PQ_prepare_statements ((pg)->conn, \ - _pi_ps)) \ - { \ - GNUNET_break (0); \ - return GNUNET_DB_STATUS_HARD_ERROR; \ - } \ - _pi_cache[_pi_next].gen = (pg)->prep_gen; \ - _pi_cache[_pi_next].serial = \ - (pg)->current_merchant_serial; \ - _pi_next = (_pi_next + 1) % PG_PREP_INSTANCE_CACHE_SLOTS; \ - } \ - } while (0) - - -/** * Check that the database connection is still up and automatically reconnects * unless we are already inside of a transaction. * diff --git a/src/backenddb/inactivate_account.c b/src/backenddb/inactivate_account.c @@ -31,6 +31,7 @@ TALER_MERCHANTDB_inactivate_account (struct TALER_MERCHANTDB_PostgresContext *pg const struct TALER_MerchantWireHashP *h_wire) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (merchant_id), GNUNET_PQ_query_param_auto_from_type (h_wire), GNUNET_PQ_query_param_end }; @@ -41,20 +42,15 @@ TALER_MERCHANTDB_inactivate_account (struct TALER_MERCHANTDB_PostgresContext *pg GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (merchant_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "inactivate_account", - "SELECT out_found AS found" - " FROM merchant_do_inactivate_account" - " ($1);"); + PREPARE (pg, + "inactivate_account", + "SELECT out_found AS found" + " FROM merchant_do_inactivate_account" + " ($1,$2);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "inactivate_account", params, rs); if (qs < 0) diff --git a/src/backenddb/increase_refund.c b/src/backenddb/increase_refund.c @@ -66,11 +66,6 @@ struct FindRefundContext struct TALER_MERCHANTDB_PostgresContext *pg; /** - * Resolved per-instance prepared-statement name for "find_refunds_by_coin". - */ - const char *stmt_find_refunds; - - /** * Updated to reflect total amount refunded so far. */ struct TALER_Amount refunded_amount; @@ -138,16 +133,6 @@ struct InsertRefundContext * due to legal limits? */ bool legal_capped; - - /** - * Resolved per-instance prepared-statement name for "insert_refund". - */ - const char *stmt_insert_refund; - - /** - * Resolved per-instance prepared-statement name for "find_refunds_by_coin". - */ - const char *stmt_find_refunds; }; @@ -373,8 +358,7 @@ process_deposits_for_refund_cb (void *cls, GNUNET_PQ_result_spec_end }; struct FindRefundContext ictx = { - .pg = pg, - .stmt_find_refunds = ctx->stmt_find_refunds + .pg = pg }; struct ExchangeLimit *el; @@ -411,7 +395,7 @@ process_deposits_for_refund_cb (void *cls, &ictx.refunded_amount)); ires = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - ictx.stmt_find_refunds, + "find_refunds_by_coin", params, &process_refund_cb, &ictx); @@ -576,7 +560,7 @@ process_deposits_for_refund_cb (void *cls, check_connection (pg); qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - ctx->stmt_insert_refund, + "insert_refund", params); switch (qs) { @@ -635,69 +619,63 @@ TALER_MERCHANTDB_increase_refund ( { enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; - char stmt_insert_refund[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_find_refunds[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_find_deposits[PG_PREP_INSTANCE_NAME_MAX]; struct InsertRefundContext ctx = { .pg = pg, .refund = refund, .olc = olc, .olc_cls = olc_cls, - .reason = reason, - .stmt_insert_refund = stmt_insert_refund, - .stmt_find_refunds = stmt_find_refunds + .reason = reason }; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); // FIXME: return 'refund_serial' from this INSERT statement for #10577 - PREPARE_INSTANCE (pg, - stmt_insert_refund, - "insert_refund", - "INSERT INTO merchant_refunds" - "(order_serial" - ",rtransaction_id" - ",refund_timestamp" - ",coin_pub" - ",reason" - ",refund_amount" - ") VALUES" - "($1, $2, $3, $4, $5, $6)"); - PREPARE_INSTANCE (pg, - stmt_find_refunds, - "find_refunds_by_coin", - "SELECT" - " refund_amount" - ",rtransaction_id" - " FROM merchant_refunds" - " WHERE coin_pub=$1" - " AND order_serial=$2"); - PREPARE_INSTANCE (pg, - stmt_find_deposits, - "find_deposits_for_refund", - "SELECT" - " dep.coin_pub" - ",dco.order_serial" - ",dep.amount_with_fee" - ",dco.exchange_url" - " FROM merchant_deposits dep" - " JOIN merchant_deposit_confirmations dco" - " USING (deposit_confirmation_serial)" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE order_id=$1" - " AND paid)"); + PREPARE (pg, + "insert_refund", + "INSERT INTO merchant_refunds" + "(order_serial" + ",rtransaction_id" + ",refund_timestamp" + ",coin_pub" + ",reason" + ",refund_amount" + ") VALUES" + "($1, $2, $3, $4, $5, $6)"); + PREPARE (pg, + "find_refunds_by_coin", + "SELECT" + " refund_amount" + ",rtransaction_id" + " FROM merchant_refunds" + " WHERE coin_pub=$1" + " AND order_serial=$2"); + PREPARE (pg, + "find_deposits_for_refund", + "SELECT" + " dep.coin_pub" + ",dco.order_serial" + ",dep.amount_with_fee" + ",dco.exchange_url" + " FROM merchant_deposits dep" + " JOIN merchant_deposit_confirmations dco" + " USING (deposit_confirmation_serial)" + " WHERE order_serial=" + " (SELECT order_serial" + " FROM merchant_contract_terms" + " WHERE order_id=$2" + " AND paid" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1))"); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Asked to refund %s on order %s\n", TALER_amount2s (refund), order_id); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt_find_deposits, + "find_deposits_for_refund", params, &process_deposits_for_refund_cb, &ctx); diff --git a/src/backenddb/increment_money_pots.c b/src/backenddb/increment_money_pots.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_increment_money_pots (struct TALER_MERCHANTDB_PostgresContext * const struct TALER_Amount *pot_increments) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_array_uint64 (money_pots_len, money_pot_ids, pg->conn), @@ -49,21 +50,16 @@ TALER_MERCHANTDB_increment_money_pots (struct TALER_MERCHANTDB_PostgresContext * GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "increment_money_pots", - "SELECT" - " out_not_found AS not_found" - " FROM merchant_do_increment_money_pots" - "($1,$2);"); + PREPARE (pg, + "increment_money_pots", + "SELECT" + " out_not_found AS not_found" + " FROM merchant_do_increment_money_pots" + "($1,$2,$3);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "increment_money_pots", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/insert_account.c b/src/backenddb/insert_account.c @@ -31,6 +31,7 @@ TALER_MERCHANTDB_insert_account (struct TALER_MERCHANTDB_PostgresContext *pg, const struct TALER_MERCHANTDB_AccountDetails *account_details) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (account_details->instance_id), GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire), GNUNET_PQ_query_param_auto_from_type (&account_details->salt), GNUNET_PQ_query_param_string (account_details->payto_uri.full_payto), @@ -47,32 +48,30 @@ TALER_MERCHANTDB_insert_account (struct TALER_MERCHANTDB_PostgresContext *pg, account_details->extra_wire_subject_metadata), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (account_details->instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_account", - "INSERT INTO merchant_accounts AS ma" - "(h_wire" - ",salt" - ",payto_uri" - ",credit_facade_url" - ",credit_facade_credentials" - ",active" - ",extra_wire_subject_metadata)" - " VALUES ($1, $2, $3, $4, $5::TEXT::JSONB, $6, $7)" - " ON CONFLICT(payto_uri)" - " DO UPDATE SET" - " active = true" - ",credit_facade_url = EXCLUDED.credit_facade_url" - ",credit_facade_credentials = EXCLUDED.credit_facade_credentials" - ",extra_wire_subject_metadata = EXCLUDED.extra_wire_subject_metadata" - " WHERE NOT ma.active"); + PREPARE (pg, + "insert_account", + "INSERT INTO merchant_accounts AS ma" + "(merchant_serial" + ",h_wire" + ",salt" + ",payto_uri" + ",credit_facade_url" + ",credit_facade_credentials" + ",active" + ",extra_wire_subject_metadata)" + " SELECT merchant_serial, $2, $3, $4, $5, $6::TEXT::JSONB, $7, $8" + " FROM merchant_instances" + " WHERE merchant_id=$1" + " ON CONFLICT(merchant_serial,payto_uri)" + " DO UPDATE SET" + " active = true" + ",credit_facade_url = EXCLUDED.credit_facade_url" + ",credit_facade_credentials = EXCLUDED.credit_facade_credentials" + ",extra_wire_subject_metadata = EXCLUDED.extra_wire_subject_metadata" + " WHERE NOT ma.active"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_account", params); } diff --git a/src/backenddb/insert_category.c b/src/backenddb/insert_category.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2024, 2026 Taler Systems SA + Copyright (C) 2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -34,6 +34,7 @@ TALER_MERCHANTDB_insert_category (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t *category_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (category_name), TALER_PQ_query_param_json (category_name_i18n), GNUNET_PQ_query_param_end @@ -43,23 +44,22 @@ TALER_MERCHANTDB_insert_category (struct TALER_MERCHANTDB_PostgresContext *pg, category_id), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_category", - "INSERT INTO merchant_categories" - "(category_name" - ",category_name_i18n" - ")" - " VALUES ($1, $2::TEXT::JSONB)" - " RETURNING category_serial"); + PREPARE (pg, + "insert_category", + "INSERT INTO merchant_categories" + "(merchant_serial" + ",category_name" + ",category_name_i18n" + ")" + " SELECT merchant_serial," + " $2, $3::TEXT::JSONB" + " FROM merchant_instances" + " WHERE merchant_id=$1" + " RETURNING category_serial"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_category", params, rs); } diff --git a/src/backenddb/insert_contract_terms.c b/src/backenddb/insert_contract_terms.c @@ -71,12 +71,10 @@ TALER_MERCHANTDB_insert_contract_terms (struct TALER_MERCHANTDB_PostgresContext fulfillment_url = json_string_value (json_object_get (contract_terms, "fulfillment_url")); - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), TALER_PQ_query_param_json (contract_terms), GNUNET_PQ_query_param_auto_from_type (&h_contract_terms), @@ -92,40 +90,43 @@ TALER_MERCHANTDB_insert_contract_terms (struct TALER_MERCHANTDB_PostgresContext order_serial), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - PREPARE_INSTANCE (pg, - stmt, - "insert_contract_terms", - "INSERT INTO merchant_contract_terms" - "(order_serial" - ",order_id" - ",contract_terms" - ",h_contract_terms" - ",creation_time" - ",pay_deadline" - ",refund_deadline" - ",fulfillment_url" - ",claim_token" - ",pos_key" - ",pos_algorithm)" - "SELECT" - " mo.order_serial" - ",mo.order_id" - ",$2::TEXT::JSONB" /* contract_terms */ - ",$3" /* h_contract_terms */ - ",mo.creation_time" - ",$4" /* pay_deadline */ - ",$5" /* refund_deadline */ - ",$6" /* fulfillment_url */ - ",mo.claim_token" - ",mo.pos_key" - ",mo.pos_algorithm" - " FROM merchant_orders mo" - " WHERE order_id=$1" - " RETURNING order_serial"); + PREPARE (pg, + "insert_contract_terms", + "INSERT INTO merchant_contract_terms" + "(order_serial" + ",merchant_serial" + ",order_id" + ",contract_terms" + ",h_contract_terms" + ",creation_time" + ",pay_deadline" + ",refund_deadline" + ",fulfillment_url" + ",claim_token" + ",pos_key" + ",pos_algorithm)" + "SELECT" + " mo.order_serial" + ",mo.merchant_serial" + ",mo.order_id" + ",$3::TEXT::JSONB" /* contract_terms */ + ",$4" /* h_contract_terms */ + ",mo.creation_time" + ",$5" /* pay_deadline */ + ",$6" /* refund_deadline */ + ",$7" /* fulfillment_url */ + ",mo.claim_token" + ",mo.pos_key" + ",mo.pos_algorithm" + " FROM merchant_orders mo" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " RETURNING order_serial"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_contract_terms", params, rs); } diff --git a/src/backenddb/insert_deposit.c b/src/backenddb/insert_deposit.c @@ -53,29 +53,26 @@ TALER_MERCHANTDB_insert_deposit (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - /* no preflight check here, run in transaction by caller! */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Storing deposit for coin_pub: `%s', amount_with_fee: %s\n", TALER_B2S (coin_pub), TALER_amount2s (amount_with_fee)); - GNUNET_assert (NULL != pg->current_merchant_id); - PREPARE_INSTANCE (pg, - stmt, - "insert_deposit", - "INSERT INTO merchant_deposits" - "(deposit_confirmation_serial" - ",coin_offset" - ",coin_pub" - ",coin_sig" - ",amount_with_fee" - ",deposit_fee" - ",refund_fee" - ",settlement_retry_time" - ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8)" - " ON CONFLICT DO NOTHING;"); + check_connection (pg); + PREPARE (pg, + "insert_deposit", + "INSERT INTO merchant_deposits" + "(deposit_confirmation_serial" + ",coin_offset" + ",coin_pub" + ",coin_sig" + ",amount_with_fee" + ",deposit_fee" + ",refund_fee" + ",settlement_retry_time" + ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8)" + " ON CONFLICT DO NOTHING;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_deposit", params); } diff --git a/src/backenddb/insert_deposit_confirmation.c b/src/backenddb/insert_deposit_confirmation.c @@ -47,6 +47,7 @@ TALER_MERCHANTDB_insert_deposit_confirmation (struct TALER_MERCHANTDB_PostgresCo = GNUNET_STRINGS_data_to_string_alloc (&nbo, sizeof (nbo)); 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_timestamp (&deposit_timestamp), GNUNET_PQ_query_param_string (exchange_url), @@ -54,18 +55,21 @@ TALER_MERCHANTDB_insert_deposit_confirmation (struct TALER_MERCHANTDB_PostgresCo total_without_fees), TALER_PQ_query_param_amount_with_currency (pg->conn, wire_fee), - GNUNET_PQ_query_param_auto_from_type (h_wire), /* 6 */ + GNUNET_PQ_query_param_auto_from_type (h_wire), /* 7 */ GNUNET_PQ_query_param_auto_from_type (exchange_sig), GNUNET_PQ_query_param_auto_from_type (exchange_pub), GNUNET_PQ_query_param_timestamp (&wire_transfer_deadline), GNUNET_PQ_query_param_string (nbo_str), GNUNET_PQ_query_param_end }; + bool no_instance; bool no_order; bool no_account; bool no_signkey; bool conflict; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("no_instance", + &no_instance), GNUNET_PQ_result_spec_bool ("no_order", &no_order), GNUNET_PQ_result_spec_bool ("no_account", @@ -79,11 +83,7 @@ TALER_MERCHANTDB_insert_deposit_confirmation (struct TALER_MERCHANTDB_PostgresCo GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); /* no preflight check here, run in transaction by caller! */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Storing deposit confirmation for instance `%s' h_contract_terms `%s', total_without_fees: %s and wire transfer deadline in %s\n", @@ -95,19 +95,19 @@ TALER_MERCHANTDB_insert_deposit_confirmation (struct TALER_MERCHANTDB_PostgresCo wire_transfer_deadline.abs_time), true)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_deposit_confirmation", - "SELECT " - " out_no_account AS no_account" - " ,out_no_order AS no_order" - " ,out_no_signkey AS no_signkey" - " ,out_conflict AS conflict" - " ,out_deposit_confirmation_serial AS deposit_confirmation_serial" - " FROM merchant_do_insert_deposit_confirmation" - " ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);"); + PREPARE (pg, + "insert_deposit_confirmation", + "SELECT " + " out_no_instance AS no_instance" + " ,out_no_account AS no_account" + " ,out_no_order AS no_order" + " ,out_no_signkey AS no_signkey" + " ,out_conflict AS conflict" + " ,out_deposit_confirmation_serial AS deposit_confirmation_serial" + " FROM merchant_do_insert_deposit_confirmation" + " ($1, $2 ,$3, $4, $5, $6, $7, $8, $9, $10, $11);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_deposit_confirmation", params, rs); GNUNET_free (nbo_str); @@ -116,6 +116,11 @@ TALER_MERCHANTDB_insert_deposit_confirmation (struct TALER_MERCHANTDB_PostgresCo return qs; // FIXME: in the future, return these codes to the client and // return more specific error codes to the client from the API! + if (no_instance) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + } if (no_order) { GNUNET_break (0); diff --git a/src/backenddb/insert_deposit_to_transfer.c b/src/backenddb/insert_deposit_to_transfer.c @@ -51,19 +51,16 @@ TALER_MERCHANTDB_insert_deposit_to_transfer (struct TALER_MERCHANTDB_PostgresCon &dummy), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - PREPARE_INSTANCE (pg, - stmt, - "insert_deposit_to_transfer", - "SELECT" - " out_dummy" - " FROM merchant_insert_deposit_to_transfer" - " ($1,$2,$3,$4,$5,$6,$7,$8);"); + PREPARE (pg, + "insert_deposit_to_transfer", + "SELECT" + " out_dummy" + " FROM merchant_insert_deposit_to_transfer" + " ($1,$2,$3,$4,$5,$6,$7,$8);"); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "insert_deposit_to_transfer", params, rs); } diff --git a/src/backenddb/insert_donau_instance.c b/src/backenddb/insert_donau_instance.c @@ -37,6 +37,7 @@ TALER_MERCHANTDB_insert_donau_instance (struct TALER_MERCHANTDB_PostgresContext struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (donau_url), GNUNET_PQ_query_param_string (charity->name), + GNUNET_PQ_query_param_auto_from_type (&charity->charity_pub), GNUNET_PQ_query_param_uint64 (&charity_id), TALER_PQ_query_param_amount_with_currency (pg->conn, &charity->max_per_year), @@ -45,24 +46,26 @@ TALER_MERCHANTDB_insert_donau_instance (struct TALER_MERCHANTDB_PostgresContext GNUNET_PQ_query_param_uint64 (&charity->current_year), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_donau_instance", - "INSERT INTO merchant_donau_instances" - " (donau_url" - " ,charity_name" - " ,charity_id" - " ,charity_max_per_year" - " ,charity_receipts_to_date" - " ,current_year)" - "VALUES" - " ($1, $2, $3, $4, $5, $6)" - " ON CONFLICT DO NOTHING;"); + PREPARE (pg, + "insert_donau_instance", + "INSERT INTO merchant_donau_instances" + " (donau_url" + " ,charity_name" + " ,merchant_instance_serial" + " ,charity_id" + " ,charity_max_per_year" + " ,charity_receipts_to_date" + " ,current_year)" + "VALUES" + " ($1, $2," + " (SELECT merchant_serial" + " FROM merchant_instances mi" + " WHERE mi.merchant_pub = $3)," + " $4, $5, $6, $7)" + " ON CONFLICT DO NOTHING;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_donau_instance", params); } diff --git a/src/backenddb/insert_instance.c b/src/backenddb/insert_instance.c @@ -24,7 +24,6 @@ #include <taler/taler_dbevents.h> #include <taler/taler_pq_lib.h> #include "merchant-database/insert_instance.h" -#include "merchant-database/set_instance.h" #include "helper.h" enum GNUNET_DB_QueryStatus @@ -71,9 +70,9 @@ TALER_MERCHANTDB_insert_instance ( }; struct GNUNET_PQ_QueryParam params_priv[] = { GNUNET_PQ_query_param_auto_from_type (merchant_priv), + GNUNET_PQ_query_param_string (is->id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; enum GNUNET_DB_QueryStatus qs; check_connection (pg); @@ -102,27 +101,20 @@ TALER_MERCHANTDB_insert_instance ( "VALUES" "($1,$2,$3,LOWER($4),$5,$6::TEXT::JSONB,$7::TEXT::JSONB,$8,$9,$10,$11," "$12,$13,$14,$15,$16,$17,$18,$19::time_rounder_interval)"); + PREPARE (pg, + "insert_keys", + "INSERT INTO merchant_keys" + "(merchant_priv" + ",merchant_serial)" + " SELECT $1, merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$2"); qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_instance", params); if (qs <= 0) return qs; - /* AFTER INSERT trigger has now created the merchant_instance_<N> schema. - Route to it so the merchant_keys INSERT lands in the per-instance table. */ - qs = TALER_MERCHANTDB_set_instance (pg, - is->id); - if (qs <= 0) - { - GNUNET_break (0); - return qs; - } - PREPARE_INSTANCE (pg, - stmt, - "insert_keys", - "INSERT INTO merchant_keys" - "(merchant_priv)" - " VALUES ($1)"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_keys", params_priv); } diff --git a/src/backenddb/insert_issued_token.c b/src/backenddb/insert_issued_token.c @@ -50,21 +50,18 @@ TALER_MERCHANTDB_insert_issued_token (struct TALER_MERCHANTDB_PostgresContext *p GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "issued_token_insert", - "SELECT" - " out_no_family" - " ,out_existed" - " FROM merchant_do_insert_issued_token" - " ($1, $2, $3);"); + PREPARE (pg, + "issued_token_insert", + "SELECT" + " out_no_family" + " ,out_existed" + " FROM merchant_do_insert_issued_token" + " ($1, $2, $3);"); qs = GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "issued_token_insert", params, rs); if (qs < 0) diff --git a/src/backenddb/insert_login_token.c b/src/backenddb/insert_login_token.c @@ -36,6 +36,7 @@ TALER_MERCHANTDB_insert_login_token (struct TALER_MERCHANTDB_PostgresContext *pg const char *description) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_auto_from_type (token), GNUNET_PQ_query_param_timestamp (&creation_time), GNUNET_PQ_query_param_timestamp (&expiration_time), @@ -43,24 +44,22 @@ TALER_MERCHANTDB_insert_login_token (struct TALER_MERCHANTDB_PostgresContext *pg GNUNET_PQ_query_param_string (description), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_login_token", - "INSERT INTO merchant_login_tokens" - "(token" - ",creation_time" - ",expiration_time" - ",validity_scope" - ",description" - ")" - " VALUES ($1, $2, $3, $4, $5)"); + PREPARE (pg, + "insert_login_token", + "INSERT INTO merchant_login_tokens" + "(token" + ",creation_time" + ",expiration_time" + ",validity_scope" + ",description" + ",merchant_serial" + ")" + "SELECT $2, $3, $4, $5, $6, merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_login_token", params); } diff --git a/src/backenddb/insert_money_pot.c b/src/backenddb/insert_money_pot.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_insert_money_pot (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t *money_pot_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (name), GNUNET_PQ_query_param_string (description), GNUNET_PQ_query_param_end @@ -43,23 +44,21 @@ TALER_MERCHANTDB_insert_money_pot (struct TALER_MERCHANTDB_PostgresContext *pg, money_pot_id), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_money_pot", - "INSERT INTO merchant_money_pots" - "(money_pot_name" - ",money_pot_description)" - " VALUES ($1, $2)" - " ON CONFLICT DO NOTHING" - " RETURNING money_pot_serial;"); + PREPARE (pg, + "insert_money_pot", + "INSERT INTO merchant_money_pots" + "(merchant_serial" + ",money_pot_name" + ",money_pot_description)" + " SELECT merchant_serial, $2, $3" + " FROM merchant_instances" + " WHERE merchant_id=$1" + " ON CONFLICT DO NOTHING" + " RETURNING money_pot_serial;"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_money_pot", params, rs); } diff --git a/src/backenddb/insert_order.c b/src/backenddb/insert_order.c @@ -44,6 +44,7 @@ TALER_MERCHANTDB_insert_order (struct TALER_MERCHANTDB_PostgresContext *pg, = json_string_value (json_object_get (contract_terms, "fulfillment_url")); struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_timestamp (&pay_deadline), GNUNET_PQ_query_param_auto_from_type (claim_token), @@ -62,34 +63,32 @@ TALER_MERCHANTDB_insert_order (struct TALER_MERCHANTDB_PostgresContext *pg, : GNUNET_PQ_query_param_string (fulfillment_url), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; now = GNUNET_TIME_timestamp_get (); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "inserting order: order_id: %s, instance_id: %s.\n", order_id, instance_id); - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_order", - "INSERT INTO merchant_orders" - "(order_id" - ",pay_deadline" - ",claim_token" - ",h_post_data" - ",creation_time" - ",contract_terms" - ",pos_key" - ",pos_algorithm" - ",session_id" - ",fulfillment_url)" - " VALUES" - " ($1, $2, $3, $4, $5, $6::TEXT::JSONB, $7, $8, $9, $10)"); + PREPARE (pg, + "insert_order", + "INSERT INTO merchant_orders" + "(merchant_serial" + ",order_id" + ",pay_deadline" + ",claim_token" + ",h_post_data" + ",creation_time" + ",contract_terms" + ",pos_key" + ",pos_algorithm" + ",session_id" + ",fulfillment_url)" + " SELECT merchant_serial," + " $2, $3, $4, $5, $6, $7::TEXT::JSONB, $8, $9, $10, $11" + " FROM merchant_instances" + " WHERE merchant_id=$1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_order", params); } diff --git a/src/backenddb/insert_order_blinded_sigs.c b/src/backenddb/insert_order_blinded_sigs.c @@ -40,24 +40,20 @@ TALER_MERCHANTDB_insert_order_blinded_sigs (struct TALER_MERCHANTDB_PostgresCont GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_blinded_sigs", - "INSERT INTO merchant_order_token_blinded_sigs" - " (order_serial" - " ,token_index" - " ,token_hash" - " ,token_blinded_signature" - ")" - " SELECT order_serial, $2, $3, $4" - " FROM merchant_contract_terms" - " WHERE order_id = $1"); + PREPARE (pg, + "insert_blinded_sigs", + "INSERT INTO merchant_order_token_blinded_sigs" + " (order_serial" + " ,token_index" + " ,token_hash" + " ,token_blinded_signature" + ")" + " SELECT order_serial, $2, $3, $4" + " FROM merchant_contract_terms" + " WHERE order_id = $1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_blinded_sigs", params); } diff --git a/src/backenddb/insert_order_lock.c b/src/backenddb/insert_order_lock.c @@ -34,63 +34,64 @@ TALER_MERCHANTDB_insert_order_lock (struct TALER_MERCHANTDB_PostgresContext *pg, uint32_t quantity_frac) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_string (product_id), GNUNET_PQ_query_param_uint64 (&quantity), GNUNET_PQ_query_param_uint32 (&quantity_frac), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_order_lock", - "WITH tmp AS" - " (SELECT " - " product_serial" - " ,total_stock" - " ,total_stock_frac" - " ,total_sold" - " ,total_sold_frac" - " ,total_lost" - " ,total_lost_frac" - " ,allow_fractional_quantity" - " FROM merchant_inventory" - " WHERE product_id=$2)" - " INSERT INTO merchant_order_locks" - " (product_serial" - " ,total_locked" - " ,total_locked_frac" - " ,order_serial)" - " SELECT tmp.product_serial, $3::INT8, $4::INT4, order_serial" - " FROM merchant_orders" - " CROSS JOIN tmp" - " WHERE order_id=$1" - " AND (tmp.allow_fractional_quantity OR $4 = 0)" - " AND (tmp.total_stock = 9223372036854775807" - " OR (" - " (tmp.total_stock::NUMERIC * 1000000" - " + tmp.total_stock_frac::NUMERIC)" - " - (tmp.total_sold::NUMERIC * 1000000" - " + tmp.total_sold_frac::NUMERIC)" - " - (tmp.total_lost::NUMERIC * 1000000" - " + tmp.total_lost_frac::NUMERIC)" - " >= " - " (($3::NUMERIC * 1000000) + $4::NUMERIC)" - " + (SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" - " + total_locked_frac::NUMERIC), 0)" - " FROM merchant_inventory_locks mil" - " WHERE mil.product_serial = tmp.product_serial)" - " + (SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" - " + total_locked_frac::NUMERIC), 0)" - " FROM merchant_order_locks mol" - " WHERE mol.product_serial = tmp.product_serial)" - " ))"); + PREPARE (pg, + "insert_order_lock", + "WITH tmp AS" + " (SELECT " + " product_serial" + " ,merchant_serial" + " ,total_stock" + " ,total_stock_frac" + " ,total_sold" + " ,total_sold_frac" + " ,total_lost" + " ,total_lost_frac" + " ,allow_fractional_quantity" + " FROM merchant_inventory" + " WHERE product_id=$3" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1))" + " INSERT INTO merchant_order_locks" + " (product_serial" + " ,total_locked" + " ,total_locked_frac" + " ,order_serial)" + " SELECT tmp.product_serial, $4::INT8, $5::INT4, order_serial" + " FROM merchant_orders" + " JOIN tmp USING(merchant_serial)" + " WHERE order_id=$2" + " AND (tmp.allow_fractional_quantity OR $5 = 0)" + " AND (tmp.total_stock = 9223372036854775807" + " OR (" + " (tmp.total_stock::NUMERIC * 1000000" + " + tmp.total_stock_frac::NUMERIC)" + " - (tmp.total_sold::NUMERIC * 1000000" + " + tmp.total_sold_frac::NUMERIC)" + " - (tmp.total_lost::NUMERIC * 1000000" + " + tmp.total_lost_frac::NUMERIC)" + " >= " + " (($4::NUMERIC * 1000000) + $5::NUMERIC)" + " + (SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" + " + total_locked_frac::NUMERIC), 0)" + " FROM merchant_inventory_locks mil" + " WHERE mil.product_serial = tmp.product_serial)" + " + (SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" + " + total_locked_frac::NUMERIC), 0)" + " FROM merchant_order_locks mol" + " WHERE mol.product_serial = tmp.product_serial)" + " ))"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_order_lock", params); } diff --git a/src/backenddb/insert_otp.c b/src/backenddb/insert_otp.c @@ -43,6 +43,7 @@ TALER_MERCHANTDB_insert_otp (struct TALER_MERCHANTDB_PostgresContext *pg, { uint32_t pos32 = (uint32_t) td->otp_algorithm; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (otp_id), GNUNET_PQ_query_param_string (td->otp_description), GNUNET_PQ_query_param_string (td->otp_key), @@ -50,24 +51,23 @@ TALER_MERCHANTDB_insert_otp (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_uint64 (&td->otp_ctr), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_otp", - "INSERT INTO merchant_otp_devices" - "(otp_id" - ",otp_description" - ",otp_key" - ",otp_algorithm" - ",otp_ctr" - ")" - " VALUES ($1, $2, $3, $4, $5)"); + PREPARE (pg, + "insert_otp", + "INSERT INTO merchant_otp_devices" + "(merchant_serial" + ",otp_id" + ",otp_description" + ",otp_key" + ",otp_algorithm" + ",otp_ctr" + ")" + " SELECT merchant_serial," + " $2, $3, $4, $5, $6" + " FROM merchant_instances" + " WHERE merchant_id=$1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_otp", params); } diff --git a/src/backenddb/insert_pending_webhook.c b/src/backenddb/insert_pending_webhook.c @@ -35,6 +35,7 @@ TALER_MERCHANTDB_insert_pending_webhook (struct TALER_MERCHANTDB_PostgresContext const char *body) { 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), @@ -46,25 +47,23 @@ TALER_MERCHANTDB_insert_pending_webhook (struct TALER_MERCHANTDB_PostgresContext : GNUNET_PQ_query_param_string (body), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_pending_webhook", - "INSERT INTO merchant_pending_webhooks" - "(webhook_serial" - ",url" - ",http_method" - ",header" - ",body" - ")" - " VALUES ($1, $2, $3, $4, $5)"); + PREPARE (pg, + "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"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_pending_webhook", params); } diff --git a/src/backenddb/insert_product.c b/src/backenddb/insert_product.c @@ -41,34 +41,35 @@ TALER_MERCHANTDB_insert_product (struct TALER_MERCHANTDB_PostgresContext *pg, bool *no_pot) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (product_id), GNUNET_PQ_query_param_string (pd->description), - TALER_PQ_query_param_json (pd->description_i18n), /* $3 */ + TALER_PQ_query_param_json (pd->description_i18n), /* $4 */ GNUNET_PQ_query_param_string (pd->unit), GNUNET_PQ_query_param_string (pd->image), - TALER_PQ_query_param_json (pd->taxes), /* $6 */ + TALER_PQ_query_param_json (pd->taxes), /* $7 */ TALER_PQ_query_param_array_amount_with_currency ( pd->price_array_length, pd->price_array, - pg->conn), /* $7 */ - GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $8 */ - GNUNET_PQ_query_param_uint32 (&pd->total_stock_frac), /* $9 */ + pg->conn), /* $8 */ + GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $9 */ + GNUNET_PQ_query_param_uint32 (&pd->total_stock_frac), /* $10 */ GNUNET_PQ_query_param_bool (pd->allow_fractional_quantity), GNUNET_PQ_query_param_uint32 (&pd->fractional_precision_level), - TALER_PQ_query_param_json (pd->address), /* $12 */ + TALER_PQ_query_param_json (pd->address), /* $13 */ GNUNET_PQ_query_param_timestamp (&pd->next_restock), GNUNET_PQ_query_param_uint32 (&pd->minimum_age), GNUNET_PQ_query_param_array_uint64 (num_cats, cats, - pg->conn), /* $15 */ + pg->conn), /* $16 */ GNUNET_PQ_query_param_string (pd->product_name), (0 == pd->product_group_id) ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_uint64 (&pd->product_group_id), /* $17 */ + : GNUNET_PQ_query_param_uint64 (&pd->product_group_id), /* $18 */ (0 == pd->money_pot_id) ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_uint64 (&pd->money_pot_id), /* $18 */ - GNUNET_PQ_query_param_bool (pd->price_is_net), /* $19 */ + : GNUNET_PQ_query_param_uint64 (&pd->money_pot_id), /* $19 */ + GNUNET_PQ_query_param_bool (pd->price_is_net), /* $20 */ GNUNET_PQ_query_param_end }; uint64_t ncat; @@ -76,6 +77,8 @@ TALER_MERCHANTDB_insert_product (struct TALER_MERCHANTDB_PostgresContext *pg, struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_bool ("conflict", conflict), + GNUNET_PQ_result_spec_bool ("no_instance", + no_instance), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_uint64 ("no_cat", &ncat), @@ -87,27 +90,22 @@ TALER_MERCHANTDB_insert_product (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - *no_instance = false; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_product", - "SELECT" - " out_conflict AS conflict" - ",out_no_cat AS no_cat" - ",out_no_group AS no_group" - ",out_no_pot AS no_pot" - " FROM merchant_do_insert_product" - "($1, $2, $3::TEXT::JSONB, $4, $5, $6::TEXT::JSONB, $7" - ",$8, $9, $10, $11, $12::TEXT::JSONB, $13, $14, $15" - ",$16, $17, $18, $19);"); + PREPARE (pg, + "insert_product", + "SELECT" + " out_conflict AS conflict" + ",out_no_instance AS no_instance" + ",out_no_cat AS no_cat" + ",out_no_group AS no_group" + ",out_no_pot AS no_pot" + " FROM merchant_do_insert_product" + "($1, $2, $3, $4::TEXT::JSONB, $5, $6, $7::TEXT::JSONB, $8" + ",$9, $10, $11, $12, $13::TEXT::JSONB, $14, $15, $16" + ",$17, $18, $19, $20);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_product", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/insert_product_group.c b/src/backenddb/insert_product_group.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_insert_product_group (struct TALER_MERCHANTDB_PostgresContext * uint64_t *product_group_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (name), GNUNET_PQ_query_param_string (description), GNUNET_PQ_query_param_end @@ -43,24 +44,22 @@ TALER_MERCHANTDB_insert_product_group (struct TALER_MERCHANTDB_PostgresContext * product_group_id), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_product_group", - "INSERT INTO merchant_product_groups" - "(product_group_name" - ",product_group_description)" - " VALUES ($1, $2)" - " ON CONFLICT DO NOTHING" - " RETURNING product_group_serial"); + PREPARE (pg, + "insert_product_group", + "INSERT INTO merchant_product_groups" + "(merchant_serial" + ",product_group_name" + ",product_group_description)" + " SELECT merchant_serial, $2, $3" + " FROM merchant_instances" + " WHERE merchant_id=$1" + " ON CONFLICT DO NOTHING" + " RETURNING product_group_serial"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_product_group", params, rs); } diff --git a/src/backenddb/insert_refund_proof.c b/src/backenddb/insert_refund_proof.c @@ -38,24 +38,20 @@ TALER_MERCHANTDB_insert_refund_proof (struct TALER_MERCHANTDB_PostgresContext *p GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_refund_proof", - "INSERT INTO merchant_refund_proofs" - "(refund_serial" - ",exchange_sig" - ",signkey_serial)" - "SELECT $1, $2, signkey_serial" - " FROM merchant.merchant_exchange_signing_keys" - " WHERE exchange_pub=$3" - " ORDER BY start_date DESC" - " LIMIT 1"); + PREPARE (pg, + "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"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_refund_proof", params); } diff --git a/src/backenddb/insert_report.c b/src/backenddb/insert_report.c @@ -43,6 +43,7 @@ TALER_MERCHANTDB_insert_report ( struct TALER_MERCHANT_ReportToken report_token; struct GNUNET_TIME_Timestamp start; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (report_program_section), GNUNET_PQ_query_param_string (report_description), GNUNET_PQ_query_param_string (mime_type), @@ -59,11 +60,7 @@ TALER_MERCHANTDB_insert_report ( report_id), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &report_token, sizeof (report_token)); @@ -76,25 +73,27 @@ TALER_MERCHANTDB_insert_report ( frequency_shift))); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_report", - "INSERT INTO merchant_reports" - "(report_program_section" - ",report_description" - ",mime_type" - ",report_token" - ",data_source" - ",target_address" - ",frequency" - ",frequency_shift" - ",next_transmission)" - " VALUES ($1, $2, $3, $4," - " $5, $6, $7, $8, $9)" - " ON CONFLICT DO NOTHING;"); + PREPARE (pg, + "insert_report", + "INSERT INTO merchant_reports" + "(merchant_serial" + ",report_program_section" + ",report_description" + ",mime_type" + ",report_token" + ",data_source" + ",target_address" + ",frequency" + ",frequency_shift" + ",next_transmission)" + " SELECT merchant_serial, $2, $3, $4, $5," + " $6, $7, $8, $9, $10" + " FROM merchant_instances" + " WHERE merchant_id=$1" + " ON CONFLICT DO NOTHING;"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_report", params, rs); } diff --git a/src/backenddb/insert_spent_token.c b/src/backenddb/insert_spent_token.c @@ -52,24 +52,21 @@ TALER_MERCHANTDB_insert_spent_token (struct TALER_MERCHANTDB_PostgresContext *pg GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Storing token spent with key %s\n", GNUNET_h2s (&h_issue_pub->hash)); - PREPARE_INSTANCE (pg, - stmt, - "spent_token_insert", - "SELECT" - " out_no_family" - " ,out_conflict" - " FROM merchant_do_insert_spent_token" - "($1, $2, $3, $4, $5);"); + PREPARE (pg, + "spent_token_insert", + "SELECT" + " out_no_family" + " ,out_conflict" + " FROM merchant_do_insert_spent_token" + "($1, $2, $3, $4, $5);"); qs = GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "spent_token_insert", params, rs); if (qs < 0) diff --git a/src/backenddb/insert_template.c b/src/backenddb/insert_template.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_insert_template (struct TALER_MERCHANTDB_PostgresContext *pg, const struct TALER_MERCHANTDB_TemplateDetails *td) { 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), (0 == otp_serial_id) @@ -46,26 +47,24 @@ TALER_MERCHANTDB_insert_template (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_template", - "INSERT INTO merchant_template" - "(template_id" - ",template_description" - ",otp_device_id" - ",template_contract" - ",editable_defaults" - ")" - " VALUES" - " ($1, $2, $3, $4::TEXT::JSONB, $5::TEXT::JSONB)"); + PREPARE (pg, + "insert_template", + "INSERT INTO merchant_template" + "(merchant_serial" + ",template_id" + ",template_description" + ",otp_device_id" + ",template_contract" + ",editable_defaults" + ")" + " SELECT merchant_serial," + " $2, $3, $4, $5::TEXT::JSONB, $6::TEXT::JSONB" + " FROM merchant_instances" + " WHERE merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_template", params); GNUNET_PQ_cleanup_query_params_closures (params); return qs; diff --git a/src/backenddb/insert_token_family.c b/src/backenddb/insert_token_family.c @@ -46,52 +46,48 @@ TALER_MERCHANTDB_insert_token_family (struct TALER_MERCHANTDB_PostgresContext *p GNUNET_break (0); return GNUNET_DB_STATUS_HARD_ERROR; } - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); + PREPARE (pg, + "insert_token_family", + "INSERT INTO merchant_token_families" + "(merchant_serial" + ",slug" + ",name" + ",description" + ",description_i18n" + ",extra_data" + ",valid_after" + ",valid_before" + ",duration" + ",validity_granularity" + ",start_offset" + ",kind)" + " SELECT merchant_serial, $2, $3, $4, $5::TEXT::JSONB," + " $6::TEXT::JSONB, $7, $8, $9, $10, $11, $12" + " FROM merchant_instances" + " WHERE merchant_id=$1" + " ON CONFLICT DO NOTHING;"); { - char stmt[PG_PREP_INSTANCE_NAME_MAX]; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), + GNUNET_PQ_query_param_string (token_family_slug), + GNUNET_PQ_query_param_string (details->name), + GNUNET_PQ_query_param_string (details->description), + TALER_PQ_query_param_json (details->description_i18n), + NULL == details->extra_data + ? GNUNET_PQ_query_param_null () + : TALER_PQ_query_param_json (details->extra_data), + GNUNET_PQ_query_param_timestamp (&details->valid_after), + GNUNET_PQ_query_param_timestamp (&details->valid_before), + GNUNET_PQ_query_param_relative_time (&details->duration), + GNUNET_PQ_query_param_relative_time (&details->validity_granularity), + GNUNET_PQ_query_param_relative_time (&details->start_offset), + GNUNET_PQ_query_param_string (kind), + GNUNET_PQ_query_param_end + }; - PREPARE_INSTANCE (pg, - stmt, - "insert_token_family", - "INSERT INTO merchant_token_families" - "(slug" - ",name" - ",description" - ",description_i18n" - ",extra_data" - ",valid_after" - ",valid_before" - ",duration" - ",validity_granularity" - ",start_offset" - ",kind)" - " VALUES ($1, $2, $3, $4::TEXT::JSONB," - " $5::TEXT::JSONB, $6, $7, $8, $9, $10, $11)" - " ON CONFLICT DO NOTHING;"); - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (token_family_slug), - GNUNET_PQ_query_param_string (details->name), - GNUNET_PQ_query_param_string (details->description), - TALER_PQ_query_param_json (details->description_i18n), - NULL == details->extra_data - ? GNUNET_PQ_query_param_null () - : TALER_PQ_query_param_json (details->extra_data), - GNUNET_PQ_query_param_timestamp (&details->valid_after), - GNUNET_PQ_query_param_timestamp (&details->valid_before), - GNUNET_PQ_query_param_relative_time (&details->duration), - GNUNET_PQ_query_param_relative_time (&details->validity_granularity), - GNUNET_PQ_query_param_relative_time (&details->start_offset), - GNUNET_PQ_query_param_string (kind), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, - params); - } + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_token_family", + params); } } diff --git a/src/backenddb/insert_token_family_key.c b/src/backenddb/insert_token_family_key.c @@ -89,53 +89,50 @@ TALER_MERCHANTDB_insert_token_family_key (struct TALER_MERCHANTDB_PostgresContex valid_after.abs_time)); GNUNET_assert (! GNUNET_TIME_absolute_is_zero ( valid_before.abs_time)); - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (merchant_id, - pg->current_merchant_id)); + PREPARE (pg, + "token_family_key_insert", + "INSERT INTO merchant_token_family_keys " + "(token_family_serial" + ",pub" + ",h_pub" + ",priv" + ",private_key_created_at" + ",private_key_deleted_at" + ",signature_validity_start" + ",signature_validity_end" + ",cipher)" + " SELECT token_family_serial, $2, $3, $4, $5, $6, $7, $8, $9" + " FROM merchant_token_families" + " WHERE (slug = $1)" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$10)"); { - char stmt[PG_PREP_INSTANCE_NAME_MAX]; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (token_family_slug), + GNUNET_PQ_query_param_blind_sign_pub (pub->public_key), + GNUNET_PQ_query_param_auto_from_type (&pub->public_key->pub_key_hash), + GNUNET_PQ_query_param_blind_sign_priv (priv->private_key), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_timestamp (&key_expires), + GNUNET_PQ_query_param_timestamp (&valid_after), + GNUNET_PQ_query_param_timestamp (&valid_before), + GNUNET_PQ_query_param_string (cipher), + GNUNET_PQ_query_param_string (merchant_id), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; - PREPARE_INSTANCE (pg, - stmt, - "token_family_key_insert", - "INSERT INTO merchant_token_family_keys " - "(token_family_serial" - ",pub" - ",h_pub" - ",priv" - ",private_key_created_at" - ",private_key_deleted_at" - ",signature_validity_start" - ",signature_validity_end" - ",cipher)" - " SELECT token_family_serial, $2, $3, $4, $5, $6, $7, $8, $9" - " FROM merchant_token_families" - " WHERE (slug = $1)"); - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (token_family_slug), - GNUNET_PQ_query_param_blind_sign_pub (pub->public_key), - GNUNET_PQ_query_param_auto_from_type (&pub->public_key->pub_key_hash), - GNUNET_PQ_query_param_blind_sign_priv (priv->private_key), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_timestamp (&key_expires), - GNUNET_PQ_query_param_timestamp (&valid_after), - GNUNET_PQ_query_param_timestamp (&valid_before), - GNUNET_PQ_query_param_string (cipher), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, - params); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Insert into MTFK %s with valid [%llu,%llu] got %d\n", - token_family_slug, - (unsigned long long) valid_after.abs_time.abs_value_us, - (unsigned long long) valid_before.abs_time.abs_value_us, - (int) qs); - return qs; - } + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "token_family_key_insert", + params); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Insert into MTFK %s with valid [%llu,%llu] got %d\n", + token_family_slug, + (unsigned long long) valid_after.abs_time.abs_value_us, + (unsigned long long) valid_before.abs_time.abs_value_us, + (int) qs); + return qs; } } diff --git a/src/backenddb/insert_transfer.c b/src/backenddb/insert_transfer.c @@ -40,6 +40,7 @@ TALER_MERCHANTDB_insert_transfer (struct TALER_MERCHANTDB_PostgresContext *pg, { 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_string (exchange_url), GNUNET_PQ_query_param_auto_from_type (wtid), TALER_PQ_query_param_amount_with_currency (pg->conn, @@ -52,29 +53,26 @@ TALER_MERCHANTDB_insert_transfer (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("no_instance", + no_instance), GNUNET_PQ_result_spec_bool ("no_account", no_account), GNUNET_PQ_result_spec_bool ("conflict", conflict), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - *no_instance = false; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_transfer", - "SELECT " - " out_no_account AS no_account" - " ,out_conflict AS conflict" - " FROM merchant_do_insert_transfer" - " ($1, $2, $3, $4, $5, $6);"); + PREPARE (pg, + "insert_transfer", + "SELECT " + " out_no_instance AS no_instance" + " ,out_no_account AS no_account" + " ,out_conflict AS conflict" + " FROM merchant_do_insert_transfer" + " ($1, $2, $3, $4, $5, $6, $7);"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_transfer", params, rs); } diff --git a/src/backenddb/insert_transfer_details.c b/src/backenddb/insert_transfer_details.c @@ -48,11 +48,6 @@ TALER_MERCHANTDB_insert_transfer_details (struct TALER_MERCHANTDB_PostgresContex const struct TALER_PrivateContractHashP *contract_terms[GNUNET_NZL (len)]; enum GNUNET_DB_QueryStatus qs; bool duplicate; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); for (unsigned int i = 0; i<len; i++) { @@ -65,18 +60,18 @@ TALER_MERCHANTDB_insert_transfer_details (struct TALER_MERCHANTDB_PostgresContex } check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_transfer_details", - "SELECT" - " out_no_account" - ",out_no_exchange" - ",out_duplicate" - ",out_conflict" - ",out_order_id" - ",out_merchant_pub" - " FROM merchant_do_insert_transfer_details" - " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12);"); + PREPARE (pg, + "insert_transfer_details", + "SELECT" + " out_no_instance" + ",out_no_account" + ",out_no_exchange" + ",out_duplicate" + ",out_conflict" + ",out_order_id" + ",out_merchant_pub" + " FROM merchant_do_insert_transfer_details" + " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);"); for (unsigned int retries = 0; retries < MAX_RETRIES; @@ -92,6 +87,7 @@ TALER_MERCHANTDB_insert_transfer_details (struct TALER_MERCHANTDB_PostgresContex { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (exchange_url), GNUNET_PQ_query_param_string (payto_uri.full_payto), GNUNET_PQ_query_param_auto_from_type (wtid), @@ -120,12 +116,15 @@ TALER_MERCHANTDB_insert_transfer_details (struct TALER_MERCHANTDB_PostgresContex pg->conn), GNUNET_PQ_query_param_end }; + bool no_instance; bool no_account; bool no_exchange; bool conflict; char *order_id = NULL; struct TALER_MerchantPublicKeyP merchant_pub; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("out_no_instance", + &no_instance), GNUNET_PQ_result_spec_bool ("out_no_account", &no_account), GNUNET_PQ_result_spec_bool ("out_no_exchange", @@ -146,7 +145,7 @@ TALER_MERCHANTDB_insert_transfer_details (struct TALER_MERCHANTDB_PostgresContex }; qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_transfer_details", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); @@ -182,7 +181,8 @@ TALER_MERCHANTDB_insert_transfer_details (struct TALER_MERCHANTDB_PostgresContex GNUNET_free (order_id); } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transfer details inserted: %s%s%s%s\n", + "Transfer details inserted: %s%s%s%s%s\n", + no_instance ? "no instance " : "", no_account ? "no account " : "", no_exchange ? "no exchange ": "", duplicate ? "duplicate ": "", diff --git a/src/backenddb/insert_unclaim_signature.c b/src/backenddb/insert_unclaim_signature.c @@ -61,6 +61,7 @@ TALER_MERCHANTDB_insert_unclaim_signature ( char *notify_str = get_notify_str (order_id, merchant_pub); struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_string (nonce_str), GNUNET_PQ_query_param_string (notify_str), @@ -75,22 +76,17 @@ TALER_MERCHANTDB_insert_unclaim_signature ( GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_unclaim_signature", - "SELECT" - " out_found" - " FROM merchant_do_insert_unclaim_signature" - "($1, $2, $3, $4, $5);"); + PREPARE (pg, + "insert_unclaim_signature", + "SELECT" + " out_found" + " FROM merchant_do_insert_unclaim_signature" + "($1, $2, $3, $4, $5, $6);"); qs = GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "insert_unclaim_signature", params, rs); GNUNET_free (nonce_str); diff --git a/src/backenddb/insert_unit.c b/src/backenddb/insert_unit.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2025, 2026 Taler Systems SA + Copyright (C) 2025 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -35,6 +35,7 @@ TALER_MERCHANTDB_insert_unit (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t *unit_serial) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (ud->unit), GNUNET_PQ_query_param_string (ud->unit_name_long), GNUNET_PQ_query_param_string (ud->unit_name_short), @@ -47,6 +48,8 @@ TALER_MERCHANTDB_insert_unit (struct TALER_MERCHANTDB_PostgresContext *pg, }; bool unit_serial_present = true; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("no_instance", + no_instance), GNUNET_PQ_result_spec_bool ("conflict", conflict), GNUNET_PQ_result_spec_allow_null ( @@ -56,25 +59,21 @@ TALER_MERCHANTDB_insert_unit (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; *no_instance = false; *conflict = false; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_unit", - "SELECT" - " out_conflict AS conflict" - " ,out_unit_serial AS unit_serial" - " FROM merchant_do_insert_unit" - " ($1,$2,$3,$4,$5,$6,$7,$8);"); + PREPARE (pg, + "insert_unit", + "SELECT" + " out_no_instance AS no_instance" + " ,out_conflict AS conflict" + " ,out_unit_serial AS unit_serial" + " FROM merchant_do_insert_unit" + " ($1,$2,$3,$4,$5,$6,$7,$8,$9);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "insert_unit", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/insert_webhook.c b/src/backenddb/insert_webhook.c @@ -32,6 +32,7 @@ TALER_MERCHANTDB_insert_webhook (struct TALER_MERCHANTDB_PostgresContext *pg, const struct TALER_MERCHANTDB_WebhookDetails *wb) { 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), @@ -44,26 +45,25 @@ TALER_MERCHANTDB_insert_webhook (struct TALER_MERCHANTDB_PostgresContext *pg, : GNUNET_PQ_query_param_string (wb->body_template), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "insert_webhook", - "INSERT INTO merchant_webhook" - "(webhook_id" - ",event_type" - ",url" - ",http_method" - ",header_template" - ",body_template" - ")" - " VALUES ($1, $2, $3, $4, $5, $6)"); + PREPARE (pg, + "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"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "insert_webhook", params); } diff --git a/src/backenddb/lock_product.c b/src/backenddb/lock_product.c @@ -35,6 +35,7 @@ TALER_MERCHANTDB_lock_product (struct TALER_MERCHANTDB_PostgresContext *pg, struct GNUNET_TIME_Timestamp expiration_time) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (product_id), GNUNET_PQ_query_param_auto_from_type (uuid), GNUNET_PQ_query_param_uint64 (&quantity), @@ -42,56 +43,59 @@ TALER_MERCHANTDB_lock_product (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_timestamp (&expiration_time), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lock_product", - "WITH tmp AS" - " (SELECT" - " mi.product_serial" - " ,mi.total_stock" - " ,mi.total_stock_frac" - " ,mi.total_sold" - " ,mi.total_sold_frac" - " ,mi.total_lost" - " ,mi.total_lost_frac" - " ,mi.allow_fractional_quantity" - " FROM merchant_inventory mi" - " WHERE mi.product_id=$1)" - "INSERT INTO merchant_inventory_locks" - "(product_serial" - ",lock_uuid" - ",total_locked" - ",total_locked_frac" - ",expiration)" - " SELECT tmp.product_serial, $2, $3::INT8, $4::INT4, $5" - " FROM tmp" - " WHERE (tmp.allow_fractional_quantity OR $4 = 0)" - " AND (tmp.total_stock = 9223372036854775807" - " OR (" - " (tmp.total_stock::NUMERIC * 1000000" - " + tmp.total_stock_frac::NUMERIC)" - " - (tmp.total_sold::NUMERIC * 1000000" - " + tmp.total_sold_frac::NUMERIC)" - " - (tmp.total_lost::NUMERIC * 1000000" - " + tmp.total_lost_frac::NUMERIC)" - " >= " - " (($3::NUMERIC * 1000000) + $4::NUMERIC)" - " + (SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" - " + total_locked_frac::NUMERIC), 0)" - " FROM merchant_inventory_locks mil" - " WHERE mil.product_serial = tmp.product_serial)" - " + (SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" - " + total_locked_frac::NUMERIC), 0)" - " FROM merchant_order_locks mol" - " WHERE mol.product_serial = tmp.product_serial)" - " ))"); + PREPARE (pg, + "lock_product", + "WITH ps AS" + " (SELECT product_serial" + " FROM merchant_inventory" + " WHERE product_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1))" + ",tmp AS" + " (SELECT" + " mi.product_serial" + " ,mi.total_stock" + " ,mi.total_stock_frac" + " ,mi.total_sold" + " ,mi.total_sold_frac" + " ,mi.total_lost" + " ,mi.total_lost_frac" + " ,mi.allow_fractional_quantity" + " FROM merchant_inventory mi" + " JOIN ps USING (product_serial))" + "INSERT INTO merchant_inventory_locks" + "(product_serial" + ",lock_uuid" + ",total_locked" + ",total_locked_frac" + ",expiration)" + " SELECT tmp.product_serial, $3, $4::INT8, $5::INT4, $6" + " FROM tmp" + " WHERE (tmp.allow_fractional_quantity OR $5 = 0)" + " AND (tmp.total_stock = 9223372036854775807" + " OR (" + " (tmp.total_stock::NUMERIC * 1000000" + " + tmp.total_stock_frac::NUMERIC)" + " - (tmp.total_sold::NUMERIC * 1000000" + " + tmp.total_sold_frac::NUMERIC)" + " - (tmp.total_lost::NUMERIC * 1000000" + " + tmp.total_lost_frac::NUMERIC)" + " >= " + " (($4::NUMERIC * 1000000) + $5::NUMERIC)" + " + (SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" + " + total_locked_frac::NUMERIC), 0)" + " FROM merchant_inventory_locks mil" + " WHERE mil.product_serial = tmp.product_serial)" + " + (SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" + " + total_locked_frac::NUMERIC), 0)" + " FROM merchant_order_locks mol" + " WHERE mol.product_serial = tmp.product_serial)" + " ))"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "lock_product", params); } diff --git a/src/backenddb/lookup_account.c b/src/backenddb/lookup_account.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2023, 2026 Taler Systems SA + Copyright (C) 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -32,6 +32,7 @@ TALER_MERCHANTDB_lookup_account (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t *account_serial) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (payto_uri.full_payto), GNUNET_PQ_query_param_end }; @@ -40,22 +41,21 @@ TALER_MERCHANTDB_lookup_account (struct TALER_MERCHANTDB_PostgresContext *pg, account_serial), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_account", - "SELECT" - " account_serial" - " FROM merchant_accounts" - " WHERE REGEXP_REPLACE(payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($1,'\\?.*','')"); + PREPARE (pg, + "lookup_account", + "SELECT" + " account_serial" + " FROM merchant_accounts" + " WHERE REGEXP_REPLACE(payto_uri,'\\?.*','')" + " =REGEXP_REPLACE($2,'\\?.*','')" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_account", params, rs); } diff --git a/src/backenddb/lookup_all_products.c b/src/backenddb/lookup_all_products.c @@ -173,54 +173,53 @@ TALER_MERCHANTDB_lookup_all_products ( .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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_all_products", - "SELECT" - " description" - ",description_i18n::TEXT" - ",product_name" - ",unit" - ",price_array" - ",taxes::TEXT" - ",total_stock" - ",total_stock_frac" - ",allow_fractional_quantity" - ",fractional_precision_level" - ",total_sold" - ",total_sold_frac" - ",total_lost" - ",total_lost_frac" - ",image" - ",minv.address::TEXT" - ",next_restock" - ",minimum_age" - ",product_id" - ",product_serial" - ",t.category_array AS categories" - ",product_group_serial" - ",money_pot_serial" - ",price_is_net" - " FROM merchant_inventory minv" - ",LATERAL (" - " SELECT ARRAY (" - " SELECT mpc.category_serial" - " FROM merchant_product_categories mpc" - " WHERE mpc.product_serial = minv.product_serial" - " ) AS category_array" - " ) t"); + PREPARE (pg, + "lookup_all_products", + "SELECT" + " description" + ",description_i18n::TEXT" + ",product_name" + ",unit" + ",price_array" + ",taxes::TEXT" + ",total_stock" + ",total_stock_frac" + ",allow_fractional_quantity" + ",fractional_precision_level" + ",total_sold" + ",total_sold_frac" + ",total_lost" + ",total_lost_frac" + ",image" + ",minv.address::TEXT" + ",next_restock" + ",minimum_age" + ",product_id" + ",product_serial" + ",t.category_array AS categories" + ",product_group_serial" + ",money_pot_serial" + ",price_is_net" + " FROM merchant_inventory minv" + " JOIN merchant_instances inst" + " USING (merchant_serial)" + ",LATERAL (" + " SELECT ARRAY (" + " SELECT mpc.category_serial" + " FROM merchant_product_categories mpc" + " WHERE mpc.product_serial = minv.product_serial" + " ) AS category_array" + " ) t" + " WHERE inst.merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_all_products", params, &lookup_products_cb, &plc); diff --git a/src/backenddb/lookup_categories.c b/src/backenddb/lookup_categories.c @@ -113,31 +113,30 @@ TALER_MERCHANTDB_lookup_categories (struct TALER_MERCHANTDB_PostgresContext *pg, .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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_categories", - "SELECT" - " mc.category_serial" - ",mc.category_name" - ",mc.category_name_i18n::TEXT" - ",COALESCE(COUNT(mpc.product_serial),0)" - " AS product_count" - " FROM merchant_categories mc" - " LEFT JOIN merchant_product_categories mpc" - " USING (category_serial)" - " GROUP BY mc.category_serial" - " ORDER BY mc.category_serial;"); + PREPARE (pg, + "lookup_categories", + "SELECT" + " mc.category_serial" + ",mc.category_name" + ",mc.category_name_i18n::TEXT" + ",COALESCE(COUNT(mpc.product_serial),0)" + " AS product_count" + " FROM merchant_categories mc" + " LEFT JOIN merchant_product_categories mpc" + " USING (category_serial)" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mi.merchant_id=$1" + " GROUP BY mc.category_serial" + " ORDER BY mc.category_serial;"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_categories", params, &lookup_categories_cb, &tlc); diff --git a/src/backenddb/lookup_categories_by_ids.c b/src/backenddb/lookup_categories_by_ids.c @@ -107,35 +107,34 @@ TALER_MERCHANTDB_lookup_categories_by_ids (struct TALER_MERCHANTDB_PostgresConte .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_array_uint64 (num_category_ids, category_ids, pg->conn), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_categories_by_ids", - "SELECT" - " mc.category_serial" - ",mc.category_name" - ",mc.category_name_i18n::TEXT" - ",COALESCE(COUNT(mpc.product_serial),0)" - " AS product_count" - " FROM merchant_categories mc" - " LEFT JOIN merchant_product_categories mpc" - " USING (category_serial)" - " WHERE mc.category_serial = ANY ($1)" - " GROUP BY mc.category_serial" - " ORDER BY mc.category_serial;"); + PREPARE (pg, + "lookup_categories_by_ids", + "SELECT" + " mc.category_serial" + ",mc.category_name" + ",mc.category_name_i18n::TEXT" + ",COALESCE(COUNT(mpc.product_serial),0)" + " AS product_count" + " FROM merchant_categories mc" + " LEFT JOIN merchant_product_categories mpc" + " USING (category_serial)" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mi.merchant_id=$1" + " AND mc.category_serial = ANY ($2)" + " GROUP BY mc.category_serial" + " ORDER BY mc.category_serial;"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_categories_by_ids", params, &lookup_categories_cb, &tlc); diff --git a/src/backenddb/lookup_contract_terms.c b/src/backenddb/lookup_contract_terms.c @@ -38,6 +38,7 @@ TALER_MERCHANTDB_lookup_contract_terms (struct TALER_MERCHANTDB_PostgresContext enum GNUNET_DB_QueryStatus qs; struct TALER_ClaimTokenP ct; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; @@ -51,23 +52,22 @@ TALER_MERCHANTDB_lookup_contract_terms (struct TALER_MERCHANTDB_PostgresContext &ct), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_contract_terms", - "SELECT" - " contract_terms::TEXT" - ",order_serial" - ",claim_token" - " FROM merchant_contract_terms" - " WHERE order_id=$1"); + PREPARE (pg, + "lookup_contract_terms", + "SELECT" + " contract_terms::TEXT" + ",order_serial" + ",claim_token" + " FROM merchant_contract_terms" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_contract_terms", params, (NULL != contract_terms) ? rs diff --git a/src/backenddb/lookup_contract_terms2.c b/src/backenddb/lookup_contract_terms2.c @@ -39,6 +39,7 @@ TALER_MERCHANTDB_lookup_contract_terms2 (struct TALER_MERCHANTDB_PostgresContext enum GNUNET_DB_QueryStatus qs; struct TALER_ClaimTokenP ct; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; @@ -63,26 +64,25 @@ TALER_MERCHANTDB_lookup_contract_terms2 (struct TALER_MERCHANTDB_PostgresContext NULL), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_contract_terms2", - "SELECT" - " contract_terms::TEXT" - ",order_serial" - ",claim_token" - ",paid" - ",pos_key" - ",pos_algorithm" - " FROM merchant_contract_terms" - " WHERE order_id=$1"); + PREPARE (pg, + "lookup_contract_terms2", + "SELECT" + " contract_terms::TEXT" + ",order_serial" + ",claim_token" + ",paid" + ",pos_key" + ",pos_algorithm" + " FROM merchant_contract_terms" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_contract_terms2", params, (NULL != contract_terms) ? rs diff --git a/src/backenddb/lookup_contract_terms3.c b/src/backenddb/lookup_contract_terms3.c @@ -46,6 +46,7 @@ TALER_MERCHANTDB_lookup_contract_terms3 (struct TALER_MERCHANTDB_PostgresContext uint16_t ci = 0; bool choice_index_null = false; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), NULL == session_id ? GNUNET_PQ_query_param_null () @@ -74,28 +75,27 @@ TALER_MERCHANTDB_lookup_contract_terms3 (struct TALER_MERCHANTDB_PostgresContext &choice_index_null), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); *session_matches = false; check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_contract_terms3", - "SELECT" - " contract_terms::TEXT" - ",order_serial" - ",claim_token" - ",paid" - ",wired" - ",(session_id=$2) AS session_matches" - ",choice_index" - " FROM merchant_contract_terms" - " WHERE order_id=$1"); + PREPARE (pg, + "lookup_contract_terms3", + "SELECT" + " contract_terms::TEXT" + ",order_serial" + ",claim_token" + ",paid" + ",wired" + ",(session_id=$3) AS session_matches" + ",choice_index" + " FROM merchant_contract_terms" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_contract_terms3", params, (NULL != contract_terms) ? rs diff --git a/src/backenddb/lookup_custom_units_by_names.c b/src/backenddb/lookup_custom_units_by_names.c @@ -102,36 +102,36 @@ TALER_MERCHANTDB_lookup_custom_units_by_names (struct TALER_MERCHANTDB_PostgresC .extract_failed = false }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_array_ptrs_string (num_units, (const char **) units, pg->conn), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_custom_units_by_names", - "SELECT cu.unit_serial" - " ,cu.unit" - " ,cu.unit_name_long" - " ,cu.unit_name_short" - " ,cu.unit_name_long_i18n" - " ,cu.unit_name_short_i18n" - " ,cu.unit_allow_fraction" - " ,cu.unit_precision_level" - " ,cu.unit_active" - " ,FALSE AS unit_builtin" - " FROM merchant_custom_units cu" - " WHERE cu.unit = ANY ($1)" - " ORDER BY cu.unit"); + PREPARE (pg, + "lookup_custom_units_by_names", + "WITH mi AS (" + " SELECT merchant_serial FROM merchant_instances WHERE merchant_id=$1" + ")" + "SELECT cu.unit_serial" + " ,cu.unit" + " ,cu.unit_name_long" + " ,cu.unit_name_short" + " ,cu.unit_name_long_i18n" + " ,cu.unit_name_short_i18n" + " ,cu.unit_allow_fraction" + " ,cu.unit_precision_level" + " ,cu.unit_active" + " ,FALSE AS unit_builtin" + " FROM merchant_custom_units cu" + " JOIN mi ON cu.merchant_serial = mi.merchant_serial" + " WHERE cu.unit = ANY ($2)" + " ORDER BY cu.unit"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_custom_units_by_names", params, &lookup_units_cb, &luc); diff --git a/src/backenddb/lookup_deposits.c b/src/backenddb/lookup_deposits.c @@ -118,6 +118,7 @@ TALER_MERCHANTDB_lookup_deposits ( void *cb_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 }; @@ -127,34 +128,33 @@ TALER_MERCHANTDB_lookup_deposits ( .pg = pg }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); /* no preflight check here, run in its own transaction by the caller! */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finding deposits for h_contract_terms '%s'\n", GNUNET_h2s (&h_contract_terms->hash)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_deposits", - "SELECT" - " dcom.exchange_url" - ",dep.coin_pub" - ",dep.amount_with_fee" - ",dep.deposit_fee" - ",dep.refund_fee" - " FROM merchant_deposits dep" - " JOIN merchant_deposit_confirmations dcom" - " USING (deposit_confirmation_serial)" - " WHERE dcom.order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$1)"); + PREPARE (pg, + "lookup_deposits", + "SELECT" + " dcom.exchange_url" + ",dep.coin_pub" + ",dep.amount_with_fee" + ",dep.deposit_fee" + ",dep.refund_fee" + " FROM merchant_deposits dep" + " JOIN merchant_deposit_confirmations dcom" + " USING (deposit_confirmation_serial)" + " WHERE dcom.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))"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_deposits", params, &lookup_deposits_cb, &ldc); diff --git a/src/backenddb/lookup_deposits_by_contract_and_coin.c b/src/backenddb/lookup_deposits_by_contract_and_coin.c @@ -245,6 +245,7 @@ TALER_MERCHANTDB_lookup_deposits_by_contract_and_coin ( void *cb_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 @@ -255,70 +256,71 @@ TALER_MERCHANTDB_lookup_deposits_by_contract_and_coin ( .pg = pg }; enum GNUNET_DB_QueryStatus qs; - char stmt_refunds[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_deposits[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); /* no preflight check here, run in transaction by caller! */ TALER_LOG_DEBUG ("Looking for refund of h_contract_terms %s at `%s'\n", GNUNET_h2s (&h_contract_terms->hash), instance_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_refunds, - "lookup_refunds_by_coin_and_contract", - "SELECT" - " refund_amount" - " FROM merchant_refunds" - /* Join to filter by refunds that actually - did work, not only those we approved */ - " JOIN merchant_refund_proofs" - " USING (refund_serial)" - " WHERE coin_pub=$2" - " AND order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$1)"); + PREPARE (pg, + "lookup_refunds_by_coin_and_contract", + "SELECT" + " refund_amount" + " FROM merchant_refunds" + /* Join to filter by refunds that actually + did work, not only those we approved */ + " JOIN merchant_refund_proofs" + " USING (refund_serial)" + " WHERE coin_pub=$3" + " AND 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))"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt_refunds, + "lookup_refunds_by_coin_and_contract", params, &lookup_refunds_cb, &ldcc); if (0 > qs) return qs; - PREPARE_INSTANCE (pg, - stmt_deposits, - "lookup_deposits_by_contract_and_coin", - "SELECT" - " mcon.exchange_url" - ",dep.amount_with_fee" - ",dep.deposit_fee" - ",dep.refund_fee" - ",mcon.wire_fee" - ",acc.h_wire" - ",mcon.deposit_timestamp" - ",mct.refund_deadline" - ",mcon.exchange_sig" - ",msig.exchange_pub" - " FROM merchant_contract_terms mct" - " JOIN merchant_deposit_confirmations mcon" - " USING (order_serial)" - " JOIN merchant_deposits dep" - " USING (deposit_confirmation_serial)" - " JOIN merchant_exchange_signing_keys msig" - " ON (mcon.signkey_serial=msig.signkey_serial)" - " JOIN merchant_accounts acc" - " USING (account_serial)" - " WHERE h_contract_terms=$1" - " AND dep.coin_pub=$2"); + PREPARE (pg, + "lookup_deposits_by_contract_and_coin", + "SELECT" + " mcon.exchange_url" + ",dep.amount_with_fee" + ",dep.deposit_fee" + ",dep.refund_fee" + ",mcon.wire_fee" + ",acc.h_wire" + ",mcon.deposit_timestamp" + ",mct.refund_deadline" + ",mcon.exchange_sig" + ",msig.exchange_pub" + " FROM merchant_contract_terms mct" + " JOIN merchant_deposit_confirmations mcon" + " USING (order_serial)" + " JOIN merchant_deposits dep" + " USING (deposit_confirmation_serial)" + " JOIN merchant_exchange_signing_keys msig" + " ON (mcon.signkey_serial=msig.signkey_serial)" + " JOIN merchant_accounts acc" + " USING (account_serial)" + " WHERE h_contract_terms=$2" + " AND dep.coin_pub=$3" + " AND mct.merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt_deposits, + "lookup_deposits_by_contract_and_coin", params, &lookup_deposits_by_contract_and_coin_cb, &ldcc); diff --git a/src/backenddb/lookup_deposits_by_order.c b/src/backenddb/lookup_deposits_by_order.c @@ -135,30 +135,26 @@ TALER_MERCHANTDB_lookup_deposits_by_order (struct TALER_MERCHANTDB_PostgresConte }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_deposits_by_order", - "SELECT" - " dep.deposit_serial" - ",mcon.exchange_url" - ",acc.h_wire" - ",mcon.deposit_timestamp" - ",dep.amount_with_fee" - ",dep.deposit_fee" - ",dep.coin_pub" - " FROM merchant_deposits dep" - " JOIN merchant_deposit_confirmations mcon" - " USING(deposit_confirmation_serial)" - " JOIN merchant_accounts acc" - " USING (account_serial)" - " WHERE mcon.order_serial=$1"); + PREPARE (pg, + "lookup_deposits_by_order", + "SELECT" + " dep.deposit_serial" + ",mcon.exchange_url" + ",acc.h_wire" + ",mcon.deposit_timestamp" + ",dep.amount_with_fee" + ",dep.deposit_fee" + ",dep.coin_pub" + " FROM merchant_deposits dep" + " JOIN merchant_deposit_confirmations mcon" + " USING(deposit_confirmation_serial)" + " JOIN merchant_accounts acc" + " USING (account_serial)" + " WHERE mcon.order_serial=$1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_deposits_by_order", params, &lookup_deposits_by_order_cb, &ldoc); diff --git a/src/backenddb/lookup_expected_transfer.c b/src/backenddb/lookup_expected_transfer.c @@ -40,6 +40,7 @@ TALER_MERCHANTDB_lookup_expected_transfer (struct TALER_MERCHANTDB_PostgresConte struct TALER_MasterPublicKeyP *master_pub) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&expected_incoming_serial), GNUNET_PQ_query_param_end }; @@ -66,38 +67,36 @@ TALER_MERCHANTDB_lookup_expected_transfer (struct TALER_MERCHANTDB_PostgresConte master_pub), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); *execution_time = GNUNET_TIME_UNIT_ZERO_TS; memset (expected_credit_amount, 0, sizeof (*expected_credit_amount)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_expected_transfer", - "SELECT" - " met.expected_time" - " ,met.confirmed" - " ,met.expected_credit_amount" - " ,met.exchange_url" - " ,met.wtid" - " ,ma.payto_uri" - " ,mts.execution_time" - " ,esk.master_pub" - " FROM merchant_expected_transfers met" - " JOIN merchant_exchange_signing_keys esk" - " USING (signkey_serial)" - " JOIN merchant_accounts ma" - " USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures mts" - " USING (expected_credit_serial)" - " WHERE met.expected_credit_serial=$1"); + PREPARE (pg, + "lookup_expected_transfer", + "SELECT" + " met.expected_time" + " ,met.confirmed" + " ,met.expected_credit_amount" + " ,met.exchange_url" + " ,met.wtid" + " ,ma.payto_uri" + " ,mts.execution_time" + " ,esk.master_pub" + " FROM merchant_expected_transfers met" + " JOIN merchant_exchange_signing_keys esk" + " USING (signkey_serial)" + " JOIN merchant_accounts ma" + " USING (account_serial)" + " JOIN merchant_instances inst" + " USING (merchant_serial)" + " LEFT JOIN merchant_transfer_signatures mts" + " USING (expected_credit_serial)" + " WHERE inst.merchant_id=$1" + " AND met.expected_credit_serial=$2"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_expected_transfer", params, rs); } diff --git a/src/backenddb/lookup_expected_transfers.c b/src/backenddb/lookup_expected_transfers.c @@ -171,6 +171,7 @@ TALER_MERCHANTDB_lookup_expected_transfers (struct TALER_MERCHANTDB_PostgresCont .pg = pg }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_timestamp (&before), GNUNET_PQ_query_param_timestamp (&after), GNUNET_PQ_query_param_uint64 (&offset), @@ -178,7 +179,7 @@ TALER_MERCHANTDB_lookup_expected_transfers (struct TALER_MERCHANTDB_PostgresCont NULL == payto_uri.full_payto ? GNUNET_PQ_query_param_null () /* NULL: do not filter by payto URI */ : GNUNET_PQ_query_param_string (payto_uri.full_payto), - GNUNET_PQ_query_param_bool (! by_time), /* $6: filter by time? */ + GNUNET_PQ_query_param_bool (! by_time), /* $7: filter by time? */ GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_ALL == confirmed), /* filter by confirmed? */ GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_YES == confirmed), GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_ALL == verified), /* filter by verified? */ @@ -187,84 +188,87 @@ TALER_MERCHANTDB_lookup_expected_transfers (struct TALER_MERCHANTDB_PostgresCont GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_asc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_asc, - "lookup_expected_transfers_asc", - "SELECT" - " met.expected_credit_amount" - ",met.wtid" - ",mac.payto_uri" - ",met.exchange_url" - ",met.expected_credit_serial" - ",mts.execution_time" - ",met.confirmed" - ",met.last_http_status" - ",met.last_ec" - ",met.last_detail" - " FROM merchant_expected_transfers met" - " JOIN merchant_accounts mac" - " USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures mts" - " USING (expected_credit_serial)" - " WHERE ( $6 OR " - " (mts.execution_time IS NOT NULL AND" - " mts.execution_time < $1 AND" - " mts.execution_time >= $2) )" - " AND ( (CAST($5 AS TEXT) IS NULL) OR " - " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($5,'\\?.*','')) )" - " AND ( $7 OR " - " (met.confirmed = $8) )" - " AND ( $9 OR " - " ($10 = (200=met.last_http_status) AND" - " (0=met.last_ec) ) )" - " AND (met.expected_credit_serial > $3)" - " ORDER BY met.expected_credit_serial ASC" - " LIMIT $4"); - PREPARE_INSTANCE (pg, - stmt_desc, - "lookup_expected_transfers_desc", - "SELECT" - " met.expected_credit_amount" - ",met.wtid" - ",mac.payto_uri" - ",met.exchange_url" - ",met.expected_credit_serial" - ",mts.execution_time" - ",met.confirmed" - ",met.last_http_status" - ",met.last_ec" - ",met.last_detail" - " FROM merchant_expected_transfers met" - " JOIN merchant_accounts mac" - " USING (account_serial)" - " LEFT JOIN merchant_transfer_signatures mts" - " USING (expected_credit_serial)" - " WHERE ( $6 OR " - " (mts.execution_time IS NOT NULL AND" - " mts.execution_time < $1 AND" - " mts.execution_time >= $2) )" - " AND ( (CAST($5 AS TEXT) IS NULL) OR " - " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($5,'\\?.*','')) )" - " AND ( $7 OR " - " (met.confirmed = $8) )" - " AND ( $9 OR " - " ($10 = (200=met.last_http_status) AND" - " (0=met.last_ec) ) )" - " AND (met.expected_credit_serial < $3)" - " ORDER BY met.expected_credit_serial DESC" - " LIMIT $4"); + PREPARE (pg, + "lookup_expected_transfers_asc", + "SELECT" + " met.expected_credit_amount" + ",met.wtid" + ",mac.payto_uri" + ",met.exchange_url" + ",met.expected_credit_serial" + ",mts.execution_time" + ",met.confirmed" + ",met.last_http_status" + ",met.last_ec" + ",met.last_detail" + " FROM merchant_expected_transfers met" + " JOIN merchant_accounts mac" + " USING (account_serial)" + " LEFT JOIN merchant_transfer_signatures mts" + " USING (expected_credit_serial)" + " WHERE ( $7 OR " + " (mts.execution_time IS NOT NULL AND" + " mts.execution_time < $2 AND" + " mts.execution_time >= $3) )" + " AND ( (CAST($6 AS TEXT) IS NULL) OR " + " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" + " =REGEXP_REPLACE($6,'\\?.*','')) )" + " AND ( $8 OR " + " (met.confirmed = $9) )" + " AND ( $10 OR " + " ($11 = (200=met.last_http_status) AND" + " (0=met.last_ec) ) )" + " AND merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND (met.expected_credit_serial > $4)" + " ORDER BY met.expected_credit_serial ASC" + " LIMIT $5"); + PREPARE (pg, + "lookup_expected_transfers_desc", + "SELECT" + " met.expected_credit_amount" + ",met.wtid" + ",mac.payto_uri" + ",met.exchange_url" + ",met.expected_credit_serial" + ",mts.execution_time" + ",met.confirmed" + ",met.last_http_status" + ",met.last_ec" + ",met.last_detail" + " FROM merchant_expected_transfers met" + " JOIN merchant_accounts mac" + " USING (account_serial)" + " LEFT JOIN merchant_transfer_signatures mts" + " USING (expected_credit_serial)" + " WHERE ( $7 OR " + " (mts.execution_time IS NOT NULL AND" + " mts.execution_time < $2 AND" + " mts.execution_time >= $3) )" + " AND ( (CAST($6 AS TEXT) IS NULL) OR " + " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" + " =REGEXP_REPLACE($6,'\\?.*','')) )" + " AND ( $8 OR " + " (met.confirmed = $9) )" + " AND ( $10 OR " + " ($11 = (200=met.last_http_status) AND" + " (0=met.last_ec) ) )" + " AND merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND (met.expected_credit_serial < $4)" + " ORDER BY met.expected_credit_serial DESC" + " LIMIT $5"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - (limit > 0) ? stmt_asc : stmt_desc, + (limit > 0) + ? "lookup_expected_transfers_asc" + : "lookup_expected_transfers_desc", params, &lookup_expected_transfers_cb, &ltc); diff --git a/src/backenddb/lookup_instances.c b/src/backenddb/lookup_instances.c @@ -80,61 +80,61 @@ lookup_instances_cb (void *cls, bool no_priv; char *dwtri; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("out_merchant_serial", + GNUNET_PQ_result_spec_uint64 ("merchant_serial", &instance_serial), - GNUNET_PQ_result_spec_auto_from_type ("out_merchant_pub", + GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", &merchant_pub), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("out_auth_hash", + GNUNET_PQ_result_spec_auto_from_type ("auth_hash", &ias.auth_hash), &no_auth), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("out_auth_salt", + GNUNET_PQ_result_spec_auto_from_type ("auth_salt", &ias.auth_salt), &no_salt), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("out_merchant_priv", + GNUNET_PQ_result_spec_auto_from_type ("merchant_priv", &merchant_priv), &no_priv), - GNUNET_PQ_result_spec_string ("out_merchant_id", + GNUNET_PQ_result_spec_string ("merchant_id", &is.id), - GNUNET_PQ_result_spec_string ("out_merchant_name", + GNUNET_PQ_result_spec_string ("merchant_name", &is.name), - TALER_PQ_result_spec_json ("out_address", + TALER_PQ_result_spec_json ("address", &is.address), - TALER_PQ_result_spec_json ("out_jurisdiction", + TALER_PQ_result_spec_json ("jurisdiction", &is.jurisdiction), - GNUNET_PQ_result_spec_bool ("out_use_stefan", + GNUNET_PQ_result_spec_bool ("use_stefan", &is.use_stefan), - GNUNET_PQ_result_spec_bool ("out_phone_validated", + GNUNET_PQ_result_spec_bool ("phone_validated", &is.phone_validated), - GNUNET_PQ_result_spec_bool ("out_email_validated", + GNUNET_PQ_result_spec_bool ("email_validated", &is.email_validated), GNUNET_PQ_result_spec_relative_time ( - "out_default_wire_transfer_delay", + "default_wire_transfer_delay", &is.default_wire_transfer_delay), - GNUNET_PQ_result_spec_relative_time ("out_default_pay_delay", + GNUNET_PQ_result_spec_relative_time ("default_pay_delay", &is.default_pay_delay), - GNUNET_PQ_result_spec_relative_time ("out_default_refund_delay", + GNUNET_PQ_result_spec_relative_time ("default_refund_delay", &is.default_refund_delay), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("out_website", + GNUNET_PQ_result_spec_string ("website", &is.website), NULL), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("out_email", + GNUNET_PQ_result_spec_string ("email", &is.email), NULL), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("out_phone_number", + GNUNET_PQ_result_spec_string ("phone_number", &is.phone), NULL), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("out_logo", + GNUNET_PQ_result_spec_string ("logo", &is.logo), NULL), GNUNET_PQ_result_spec_string ( - "out_default_wire_transfer_rounding_interval", + "default_wire_transfer_rounding_interval", &dwtri), GNUNET_PQ_result_spec_end }; @@ -186,8 +186,6 @@ TALER_MERCHANTDB_lookup_instances ( .pg = pg }; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_bool (active_only), - GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; @@ -196,30 +194,60 @@ TALER_MERCHANTDB_lookup_instances ( PREPARE (pg, "lookup_instances", "SELECT" - " out_merchant_serial" - " ,out_merchant_pub" - " ,out_auth_hash" - " ,out_auth_salt" - " ,out_merchant_priv" - " ,out_merchant_id" - " ,out_merchant_name" - " ,out_address::TEXT" - " ,out_jurisdiction::TEXT" - " ,out_use_stefan" - " ,out_phone_validated" - " ,out_email_validated" - " ,out_default_wire_transfer_delay" - " ,out_default_pay_delay" - " ,out_default_refund_delay" - " ,out_website" - " ,out_email" - " ,out_phone_number" - " ,out_logo" - " ,out_default_wire_transfer_rounding_interval::TEXT" - " FROM merchant.lookup_instances($1, $2)"); + " mi.merchant_serial" + ",mi.merchant_pub" + ",mi.auth_hash" + ",mi.auth_salt" + ",mi.merchant_id" + ",mi.merchant_name" + ",mi.address::TEXT" + ",mi.jurisdiction::TEXT" + ",mi.use_stefan" + ",mi.default_wire_transfer_delay" + ",mi.default_pay_delay" + ",mi.default_refund_delay" + ",mi.website" + ",mi.email" + ",mi.phone_number" + ",mi.phone_validated" + ",mi.email_validated" + ",mi.logo" + ",mi.default_wire_transfer_rounding_interval::TEXT" + ",mk.merchant_priv" + " FROM merchant_instances mi" + " LEFT JOIN merchant_keys mk" + " USING (merchant_serial)"); + PREPARE (pg, + "lookup_active_instances", + "SELECT " + " mi.merchant_serial" + ",mi.merchant_pub" + ",mi.auth_hash" + ",mi.auth_salt" + ",mi.merchant_id" + ",mi.merchant_name" + ",mi.address::TEXT" + ",mi.jurisdiction::TEXT" + ",mi.use_stefan" + ",mi.default_wire_transfer_delay" + ",mi.default_pay_delay" + ",mi.default_refund_delay" + ",mi.website" + ",mi.email" + ",mi.phone_number" + ",mi.phone_validated" + ",mi.email_validated" + ",mi.logo" + ",mi.default_wire_transfer_rounding_interval::TEXT" + ",mk.merchant_priv" + " FROM merchant_instances mi" + " JOIN merchant_keys mk" + " USING (merchant_serial)"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - "lookup_instances", + active_only + ? "lookup_active_instances" + : "lookup_instances", params, &lookup_instances_cb, &lic); @@ -242,7 +270,6 @@ TALER_MERCHANTDB_lookup_instance (struct TALER_MERCHANTDB_PostgresContext *pg, .pg = pg }; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_bool (active_only), GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_end }; @@ -252,30 +279,62 @@ TALER_MERCHANTDB_lookup_instance (struct TALER_MERCHANTDB_PostgresContext *pg, PREPARE (pg, "lookup_instance", "SELECT" - " out_merchant_serial" - " ,out_merchant_pub" - " ,out_auth_hash" - " ,out_auth_salt" - " ,out_merchant_priv" - " ,out_merchant_id" - " ,out_merchant_name" - " ,out_address::TEXT" - " ,out_jurisdiction::TEXT" - " ,out_use_stefan" - " ,out_phone_validated" - " ,out_email_validated" - " ,out_default_wire_transfer_delay" - " ,out_default_pay_delay" - " ,out_default_refund_delay" - " ,out_website" - " ,out_email" - " ,out_phone_number" - " ,out_logo" - " ,out_default_wire_transfer_rounding_interval::TEXT" - " FROM merchant.lookup_instances($1, $2)"); + " mi.merchant_serial" + ",mi.merchant_pub" + ",mi.auth_hash" + ",mi.auth_salt" + ",mi.merchant_id" + ",mi.merchant_name" + ",mi.address::TEXT" + ",mi.jurisdiction::TEXT" + ",mi.use_stefan" + ",mi.default_wire_transfer_delay" + ",mi.default_pay_delay" + ",mi.default_refund_delay" + ",mi.website" + ",mi.email" + ",mi.phone_number" + ",mi.phone_validated" + ",mi.email_validated" + ",mi.logo" + ",mi.default_wire_transfer_rounding_interval::TEXT" + ",mk.merchant_priv" + " FROM merchant_instances mi" + " LEFT JOIN merchant_keys mk" + " USING (merchant_serial)" + " WHERE merchant_id=$1"); + PREPARE (pg, + "lookup_active_instance", + "SELECT" + " mi.merchant_serial" + ",mi.merchant_pub" + ",mi.auth_hash" + ",mi.auth_salt" + ",mi.merchant_id" + ",mi.merchant_name" + ",mi.address::TEXT" + ",mi.jurisdiction::TEXT" + ",mi.use_stefan" + ",mi.default_wire_transfer_delay" + ",mi.default_pay_delay" + ",mi.default_refund_delay" + ",mi.website" + ",mi.email" + ",mi.phone_number" + ",mi.phone_validated" + ",mi.email_validated" + ",mi.logo" + ",mi.default_wire_transfer_rounding_interval::TEXT" + ",mk.merchant_priv" + " FROM merchant_instances mi" + " JOIN merchant_keys mk" + " USING (merchant_serial)" + " WHERE merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - "lookup_instance", + active_only + ? "lookup_active_instance" + : "lookup_instance", params, &lookup_instances_cb, &lic); diff --git a/src/backenddb/lookup_inventory_products.c b/src/backenddb/lookup_inventory_products.c @@ -145,75 +145,74 @@ TALER_MERCHANTDB_lookup_inventory_products ( .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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_inventory_products", - "SELECT" - " description" - ",description_i18n::TEXT" - ",product_name" - ",unit" - ",price_array" - ",CASE WHEN minv.total_stock = 9223372036854775807" - " THEN minv.total_stock" - " ELSE (GREATEST(0, rt.remaining_total) / 1000000)::INT8" - " END AS remaining_stock" - ",CASE WHEN minv.total_stock = 9223372036854775807" - " THEN 2147483647" - " ELSE mod(GREATEST(0, rt.remaining_total), 1000000)::INT4" - " END AS remaining_stock_frac" - ",taxes::TEXT" - ",image_hash" - ",allow_fractional_quantity" - ",fractional_precision_level" - ",product_id" - ",t.category_array AS categories" - " FROM merchant_inventory minv" - ",LATERAL (" - " SELECT ARRAY (" - " SELECT mpc.category_serial" - " FROM merchant_product_categories mpc" - " WHERE mpc.product_serial = minv.product_serial" - " ) AS category_array" - " ) t" - ",LATERAL (" - " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" - " + total_locked_frac::NUMERIC), 0)" - " AS total_locked" - " FROM merchant_inventory_locks mil" - " WHERE mil.product_serial = minv.product_serial" - " ) il" - ",LATERAL (" - " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" - " + total_locked_frac::NUMERIC), 0)" - " AS total_locked" - " FROM merchant_order_locks mol" - " WHERE mol.product_serial = minv.product_serial" - " ) ol" - ",LATERAL (" - " SELECT" - " (minv.total_stock::NUMERIC * 1000000" - " + minv.total_stock_frac::NUMERIC)" - " - (minv.total_sold::NUMERIC * 1000000" - " + minv.total_sold_frac::NUMERIC)" - " - (minv.total_lost::NUMERIC * 1000000" - " + minv.total_lost_frac::NUMERIC)" - " - il.total_locked" - " - ol.total_locked" - " AS remaining_total" - " ) rt"); + PREPARE (pg, + "lookup_inventory_products", + "SELECT" + " description" + ",description_i18n::TEXT" + ",product_name" + ",unit" + ",price_array" + ",CASE WHEN minv.total_stock = 9223372036854775807" + " THEN minv.total_stock" + " ELSE (GREATEST(0, rt.remaining_total) / 1000000)::INT8" + " END AS remaining_stock" + ",CASE WHEN minv.total_stock = 9223372036854775807" + " THEN 2147483647" + " ELSE mod(GREATEST(0, rt.remaining_total), 1000000)::INT4" + " END AS remaining_stock_frac" + ",taxes::TEXT" + ",image_hash" + ",allow_fractional_quantity" + ",fractional_precision_level" + ",product_id" + ",t.category_array AS categories" + " FROM merchant_inventory minv" + " JOIN merchant_instances inst" + " USING (merchant_serial)" + ",LATERAL (" + " SELECT ARRAY (" + " SELECT mpc.category_serial" + " FROM merchant_product_categories mpc" + " WHERE mpc.product_serial = minv.product_serial" + " ) AS category_array" + " ) t" + ",LATERAL (" + " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" + " + total_locked_frac::NUMERIC), 0)" + " AS total_locked" + " FROM merchant_inventory_locks mil" + " WHERE mil.product_serial = minv.product_serial" + " ) il" + ",LATERAL (" + " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" + " + total_locked_frac::NUMERIC), 0)" + " AS total_locked" + " FROM merchant_order_locks mol" + " WHERE mol.product_serial = minv.product_serial" + " ) ol" + ",LATERAL (" + " SELECT" + " (minv.total_stock::NUMERIC * 1000000" + " + minv.total_stock_frac::NUMERIC)" + " - (minv.total_sold::NUMERIC * 1000000" + " + minv.total_sold_frac::NUMERIC)" + " - (minv.total_lost::NUMERIC * 1000000" + " + minv.total_lost_frac::NUMERIC)" + " - il.total_locked" + " - ol.total_locked" + " AS remaining_total" + " ) rt" + " WHERE inst.merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_inventory_products", params, &lookup_inventory_products_cb, &plc); diff --git a/src/backenddb/lookup_inventory_products_filtered.c b/src/backenddb/lookup_inventory_products_filtered.c @@ -142,6 +142,7 @@ TALER_MERCHANTDB_lookup_inventory_products_filtered ( .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), (0 == num_product_ids) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_array_ptrs_string ( @@ -156,84 +157,82 @@ TALER_MERCHANTDB_lookup_inventory_products_filtered ( GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_inventory_products_filtered", - "SELECT" - " description" - ",description_i18n::TEXT" - ",product_name" - ",unit" - ",price_array" - ",CASE WHEN minv.total_stock = 9223372036854775807" - " THEN minv.total_stock" - " ELSE (GREATEST(0, rt.remaining_total) / 1000000)::INT8" - " END AS remaining_stock" - ",CASE WHEN minv.total_stock = 9223372036854775807" - " THEN 2147483647" - " ELSE mod(GREATEST(0, rt.remaining_total), 1000000)::INT4" - " END AS remaining_stock_frac" - ",taxes::TEXT" - ",image_hash" - ",allow_fractional_quantity" - ",fractional_precision_level" - ",product_id" - ",t.category_array AS categories" - " FROM merchant_inventory minv" - ",LATERAL (" - " SELECT ARRAY (" - " SELECT mpc.category_serial" - " FROM merchant_product_categories mpc" - " WHERE mpc.product_serial = minv.product_serial" - " ) AS category_array" - " ) t" - ",LATERAL (" - " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" - " + total_locked_frac::NUMERIC), 0)" - " AS total_locked" - " FROM merchant_inventory_locks mil" - " WHERE mil.product_serial = minv.product_serial" - " ) il" - ",LATERAL (" - " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" - " + total_locked_frac::NUMERIC), 0)" - " AS total_locked" - " FROM merchant_order_locks mol" - " WHERE mol.product_serial = minv.product_serial" - " ) ol" - ",LATERAL (" - " SELECT" - " (minv.total_stock::NUMERIC * 1000000" - " + minv.total_stock_frac::NUMERIC)" - " - (minv.total_sold::NUMERIC * 1000000" - " + minv.total_sold_frac::NUMERIC)" - " - (minv.total_lost::NUMERIC * 1000000" - " + minv.total_lost_frac::NUMERIC)" - " - il.total_locked" - " - ol.total_locked" - " AS remaining_total" - " ) rt" - " WHERE (" - " (COALESCE (array_length ($1::TEXT[], 1), 0) > 0" - " AND minv.product_id = ANY ($1::TEXT[]))" - " OR" - " (COALESCE (array_length ($2::BIGINT[], 1), 0) > 0" - " AND EXISTS (" - " SELECT 1" - " FROM merchant_product_categories mpc" - " WHERE mpc.product_serial = minv.product_serial" - " AND mpc.category_serial = ANY ($2::BIGINT[])" - " ))" - " )"); + PREPARE (pg, + "lookup_inventory_products_filtered", + "SELECT" + " description" + ",description_i18n::TEXT" + ",product_name" + ",unit" + ",price_array" + ",CASE WHEN minv.total_stock = 9223372036854775807" + " THEN minv.total_stock" + " ELSE (GREATEST(0, rt.remaining_total) / 1000000)::INT8" + " END AS remaining_stock" + ",CASE WHEN minv.total_stock = 9223372036854775807" + " THEN 2147483647" + " ELSE mod(GREATEST(0, rt.remaining_total), 1000000)::INT4" + " END AS remaining_stock_frac" + ",taxes::TEXT" + ",image_hash" + ",allow_fractional_quantity" + ",fractional_precision_level" + ",product_id" + ",t.category_array AS categories" + " FROM merchant_inventory minv" + " JOIN merchant_instances inst" + " USING (merchant_serial)" + ",LATERAL (" + " SELECT ARRAY (" + " SELECT mpc.category_serial" + " FROM merchant_product_categories mpc" + " WHERE mpc.product_serial = minv.product_serial" + " ) AS category_array" + " ) t" + ",LATERAL (" + " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" + " + total_locked_frac::NUMERIC), 0)" + " AS total_locked" + " FROM merchant_inventory_locks mil" + " WHERE mil.product_serial = minv.product_serial" + " ) il" + ",LATERAL (" + " SELECT COALESCE(SUM(total_locked::NUMERIC * 1000000" + " + total_locked_frac::NUMERIC), 0)" + " AS total_locked" + " FROM merchant_order_locks mol" + " WHERE mol.product_serial = minv.product_serial" + " ) ol" + ",LATERAL (" + " SELECT" + " (minv.total_stock::NUMERIC * 1000000" + " + minv.total_stock_frac::NUMERIC)" + " - (minv.total_sold::NUMERIC * 1000000" + " + minv.total_sold_frac::NUMERIC)" + " - (minv.total_lost::NUMERIC * 1000000" + " + minv.total_lost_frac::NUMERIC)" + " - il.total_locked" + " - ol.total_locked" + " AS remaining_total" + " ) rt" + " WHERE inst.merchant_id=$1" + " AND (" + " (COALESCE (array_length ($2::TEXT[], 1), 0) > 0" + " AND minv.product_id = ANY ($2::TEXT[]))" + " OR" + " (COALESCE (array_length ($3::BIGINT[], 1), 0) > 0" + " AND EXISTS (" + " SELECT 1" + " FROM merchant_product_categories mpc" + " WHERE mpc.product_serial = minv.product_serial" + " AND mpc.category_serial = ANY ($3::BIGINT[])" + " ))" + " )"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_inventory_products_filtered", params, &lookup_inventory_products_cb, &plc); diff --git a/src/backenddb/lookup_login_tokens.c b/src/backenddb/lookup_login_tokens.c @@ -123,52 +123,54 @@ TALER_MERCHANTDB_lookup_login_tokens (struct TALER_MERCHANTDB_PostgresContext *p .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_timestamp (&now), GNUNET_PQ_query_param_uint64 (&offset), GNUNET_PQ_query_param_uint64 (&plimit), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_asc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_asc, - "lookup_login_tokens_asc", - "SELECT" - " token" - ",serial" - ",creation_time" - ",expiration_time" - ",validity_scope" - ",description" - " FROM merchant_login_tokens" - " WHERE expiration_time > $1" - " AND serial > $2" - " ORDER BY serial ASC" - " LIMIT $3"); - PREPARE_INSTANCE (pg, - stmt_desc, - "lookup_login_tokens_desc", - "SELECT" - " token" - ",serial" - ",creation_time" - ",expiration_time" - ",validity_scope" - ",description" - " FROM merchant_login_tokens" - " WHERE expiration_time > $1" - " AND serial < $2" - " ORDER BY serial DESC" - " LIMIT $3"); + PREPARE (pg, + "lookup_login_tokens_asc", + "SELECT" + " token" + ",serial" + ",creation_time" + ",expiration_time" + ",validity_scope" + ",description" + " FROM merchant_login_tokens" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND expiration_time > $2" + " AND serial > $3" + " ORDER BY serial ASC" + " LIMIT $4"); + PREPARE (pg, + "lookup_login_tokens_desc", + "SELECT" + " token" + ",serial" + ",creation_time" + ",expiration_time" + ",validity_scope" + ",description" + " FROM merchant_login_tokens" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND expiration_time > $2" + " AND serial < $3" + " ORDER BY serial DESC" + " LIMIT $4"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - (limit > 0) ? stmt_asc : stmt_desc, + (limit > 0) + ? "lookup_login_tokens_asc" + : "lookup_login_tokens_desc", params, &lookup_login_tokens_cb, &plc); diff --git a/src/backenddb/lookup_mfa_challenge.c b/src/backenddb/lookup_mfa_challenge.c @@ -69,38 +69,39 @@ TALER_MERCHANTDB_lookup_mfa_challenge ( &chan_str), GNUNET_PQ_result_spec_string ("required_address", required_address), + GNUNET_PQ_result_spec_string ("merchant_id", + instance_name), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - PREPARE_INSTANCE (pg, - stmt, - "lookup_mfa_challenge", - "SELECT " - " op::TEXT" - " ,salt" - " ,confirmation_date" - " ,retransmission_date" - " ,retry_counter" - " ,required_address" - " ,tan_channel::TEXT" - " FROM tan_challenges" - " WHERE (challenge_id = $1)" - " AND (h_body = $2)" - " AND (expiration_date > $3)"); + PREPARE (pg, + "lookup_mfa_challenge", + "SELECT " + " tc.op::TEXT" + " ,tc.salt" + " ,tc.confirmation_date" + " ,tc.retransmission_date" + " ,tc.retry_counter" + " ,tc.required_address" + " ,tc.tan_channel::TEXT" + " ,mi.merchant_id" + " FROM tan_challenges tc" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE (challenge_id = $1)" + " AND (h_body = $2)" + " AND (expiration_date > $3)"); /* Initialize to conservative values in case qs ends up <= 0 */ *tan_channel = TALER_MERCHANT_MFA_CHANNEL_NONE; *op = TALER_MERCHANT_MFA_CO_NONE; *retry_counter = 0; qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_mfa_challenge", params, rs); if (qs <= 0) return qs; - *instance_name = GNUNET_strdup (pg->current_merchant_id); if (no_conf) *confirmation_date = GNUNET_TIME_UNIT_FOREVER_ABS; *tan_channel = TALER_MERCHANT_MFA_channel_from_string (chan_str); diff --git a/src/backenddb/lookup_order.c b/src/backenddb/lookup_order.c @@ -39,6 +39,7 @@ TALER_MERCHANTDB_lookup_order ( struct TALER_ClaimTokenP ct; enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; @@ -51,27 +52,26 @@ TALER_MERCHANTDB_lookup_order ( h_post_data), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finding contract term, order_id: '%s', instance_id: '%s'.\n", order_id, instance_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_order", - "SELECT" - " contract_terms::TEXT" - ",claim_token" - ",h_post_data" - " FROM merchant_orders" - " WHERE order_id=$1"); + PREPARE (pg, + "lookup_order", + "SELECT" + " contract_terms::TEXT" + ",claim_token" + ",h_post_data" + " FROM merchant_orders" + " WHERE merchant_orders.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_orders.order_id=$2"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_order", params, rs); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) diff --git a/src/backenddb/lookup_order_by_fulfillment.c b/src/backenddb/lookup_order_by_fulfillment.c @@ -35,6 +35,7 @@ TALER_MERCHANTDB_lookup_order_by_fulfillment (struct TALER_MERCHANTDB_PostgresCo char **order_id) { 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_bool (allow_refunded_for_repurchase), @@ -45,34 +46,33 @@ TALER_MERCHANTDB_lookup_order_by_fulfillment (struct TALER_MERCHANTDB_PostgresCo order_id), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_order_by_fulfillment", - "SELECT" - " mct.order_id" - " FROM merchant_contract_terms mct" - " LEFT JOIN merchant_refunds mref" - " USING (order_serial)" - " WHERE fulfillment_url=$1" - " AND session_id=$2" - " AND ((CAST($3 AS BOOL)) OR" - " mref.refund_serial IS NULL)" - /* Theoretically, multiple paid orders - for the same fulfillment URL could - exist for this session_id -- if a - wallet was broken and did multiple - payments without repurchase detection. - So we need to limit to 1 when returning! */ - " ORDER BY order_id DESC" - " LIMIT 1;"); + PREPARE (pg, + "lookup_order_by_fulfillment", + "SELECT" + " mct.order_id" + " FROM merchant_contract_terms mct" + " LEFT JOIN merchant_refunds mref" + " USING (order_serial)" + " WHERE fulfillment_url=$2" + " AND session_id=$3" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND ((CAST($4 AS BOOL)) OR" + " mref.refund_serial IS NULL)" + /* Theoretically, multiple paid orders + for the same fulfillment URL could + exist for this session_id -- if a + wallet was broken and did multiple + payments without repurchase detection. + So we need to limit to 1 when returning! */ + " ORDER BY order_id DESC" + " LIMIT 1;"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_order_by_fulfillment", params, rs); } diff --git a/src/backenddb/lookup_order_charity.c b/src/backenddb/lookup_order_charity.c @@ -47,6 +47,7 @@ TALER_MERCHANTDB_lookup_order_charity ( uint64_t *donau_instance_serial) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (donau_url), GNUNET_PQ_query_param_end }; @@ -66,30 +67,29 @@ TALER_MERCHANTDB_lookup_order_charity ( donau_instance_serial), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_donau_charity", - "SELECT" - " di.donau_instances_serial" - " ,di.charity_id" - " ,k.merchant_priv" - " ,dk.keys_json::TEXT" - " ,di.charity_max_per_year" - " ,di.charity_receipts_to_date" - " FROM merchant_donau_instances di" - " JOIN merchant_donau_keys dk" - " ON dk.donau_url = di.donau_url" - " CROSS JOIN merchant_keys k" - " WHERE di.donau_url = $1;"); + PREPARE (pg, + "lookup_donau_charity", + "SELECT" + " di.donau_instances_serial" + " ,di.charity_id" + " ,k.merchant_priv" + " ,dk.keys_json::TEXT" + " ,di.charity_max_per_year" + " ,di.charity_receipts_to_date" + " FROM merchant_donau_instances di" + " JOIN merchant_donau_keys dk" + " ON dk.donau_url = di.donau_url" + " JOIN merchant_instances mi" + " ON mi.merchant_serial = di.merchant_instance_serial" + " JOIN merchant_keys k" + " ON k.merchant_serial = mi.merchant_serial" + " WHERE mi.merchant_id = $1" + " AND di.donau_url = $2;"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_donau_charity", params, rs); } diff --git a/src/backenddb/lookup_order_status.c b/src/backenddb/lookup_order_status.c @@ -35,6 +35,7 @@ TALER_MERCHANTDB_lookup_order_status (struct TALER_MERCHANTDB_PostgresContext *p uint8_t paid8; enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; @@ -45,22 +46,21 @@ TALER_MERCHANTDB_lookup_order_status (struct TALER_MERCHANTDB_PostgresContext *p &paid8), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_order_status", - "SELECT" - " h_contract_terms" - ",paid" - " FROM merchant_contract_terms" - " WHERE order_id=$1"); + PREPARE (pg, + "lookup_order_status", + "SELECT" + " h_contract_terms" + ",paid" + " FROM merchant_contract_terms" + " WHERE merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND order_id=$2"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_order_status", params, rs); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) diff --git a/src/backenddb/lookup_order_status_by_serial.c b/src/backenddb/lookup_order_status_by_serial.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_lookup_order_status_by_serial (struct TALER_MERCHANTDB_Postgres bool *paid) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&order_serial), GNUNET_PQ_query_param_end }; @@ -46,24 +47,23 @@ TALER_MERCHANTDB_lookup_order_status_by_serial (struct TALER_MERCHANTDB_Postgres paid), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); *paid = false; /* just to be safe(r) */ - PREPARE_INSTANCE (pg, - stmt, - "lookup_order_status_by_serial", - "SELECT" - " h_contract_terms" - ",order_id" - ",paid" - " FROM merchant_contract_terms" - " WHERE order_serial=$1"); + PREPARE (pg, + "lookup_order_status_by_serial", + "SELECT" + " h_contract_terms" + ",order_id" + ",paid" + " FROM merchant_contract_terms" + " WHERE merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND order_serial=$2"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_order_status_by_serial", params, rs); } diff --git a/src/backenddb/lookup_order_summary.c b/src/backenddb/lookup_order_summary.c @@ -33,6 +33,7 @@ TALER_MERCHANTDB_lookup_order_summary (struct TALER_MERCHANTDB_PostgresContext * uint64_t *order_serial) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), GNUNET_PQ_query_param_end }; @@ -43,28 +44,31 @@ TALER_MERCHANTDB_lookup_order_summary (struct TALER_MERCHANTDB_PostgresContext * timestamp), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_order_summary", - "(SELECT" - " creation_time" - ",order_serial" - " FROM merchant_contract_terms" - " WHERE order_id=$1)" - "UNION" - "(SELECT" - " creation_time" - ",order_serial" - " FROM merchant_orders" - " WHERE order_id=$1)"); + PREPARE (pg, + "lookup_order_summary", + "(SELECT" + " creation_time" + ",order_serial" + " FROM merchant_contract_terms" + " WHERE merchant_contract_terms.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_contract_terms.order_id=$2)" + "UNION" + "(SELECT" + " creation_time" + ",order_serial" + " FROM merchant_orders" + " WHERE merchant_orders.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND merchant_orders.order_id=$2)"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_order_summary", params, rs); } diff --git a/src/backenddb/lookup_orders.c b/src/backenddb/lookup_orders.c @@ -111,17 +111,18 @@ TALER_MERCHANTDB_lookup_orders ( uint64_t limit = (of->delta > 0) ? of->delta : -of->delta; struct GNUNET_PQ_QueryParam params[] = { /* $1 */ + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&limit), GNUNET_PQ_query_param_uint64 (&of->start_row), GNUNET_PQ_query_param_timestamp (&of->date), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->paid)), - /* $5 */ + /* $6 */ GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->paid)), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->refunded)), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->refunded)), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_ALL == of->wired)), GNUNET_PQ_query_param_bool ((TALER_EXCHANGE_YNA_YES == of->wired)), - /* $10 */ + /* $11 */ GNUNET_PQ_query_param_bool (NULL == of->session_id), NULL == of->session_id ? GNUNET_PQ_query_param_null () @@ -136,135 +137,151 @@ TALER_MERCHANTDB_lookup_orders ( GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_inc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_dec[PG_PREP_INSTANCE_NAME_MAX]; + char stmt[128]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Looking up orders, using filter paid: %d, refunded: %d, wired: %d\n", of->paid, of->refunded, of->wired); - PREPARE_INSTANCE (pg, - stmt_dec, - "lookup_orders_dec", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - " FROM merchant_orders" - " WHERE order_serial < $2" - " AND" - " creation_time < $3" - " AND ($4 OR " - " NOT CAST($5 AS BOOL))" /* unclaimed orders are never paid */ - " AND ($6 OR " - " NOT CAST($7 AS BOOL))"/* unclaimed orders are never refunded */ - " AND ($8 OR " - " NOT CAST($9 AS BOOL))" /* unclaimed orders are never wired */ - " AND" - " order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms)" /* only select unclaimed orders */ - " AND ($10 OR " - " ($11 = session_id))" - " AND ($12 OR " - " ($13 = fulfillment_url))" - " AND ( ($14::TEXT IS NULL) OR " - " (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )" - " ORDER BY order_serial DESC" - " LIMIT $1)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - " FROM merchant_contract_terms" - " WHERE order_serial < $2" - " AND" - " creation_time < $3" - " AND ($4 OR " - " (CAST($5 AS BOOL) = paid))" - " AND ($6 OR " - " (CAST($7 AS BOOL) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))))" - " AND ($8 OR" - " (CAST($9 AS BOOL) = wired))" - " AND ($10 OR " - " ($11 = session_id))" - " AND ($12 OR " - " ($13 = fulfillment_url))" - " AND ( ($14::TEXT IS NULL) OR " - " (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )" - " ORDER BY order_serial DESC" - " LIMIT $1)" - " ORDER BY order_serial DESC" - " LIMIT $1"); + GNUNET_snprintf (stmt, + sizeof (stmt), + "lookup_orders_%s", + (of->delta > 0) ? "inc" : "dec"); + PREPARE (pg, + "lookup_orders_dec", + "(SELECT" + " order_id" + ",order_serial" + ",creation_time" + " FROM merchant_orders" + " WHERE merchant_orders.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND" + " order_serial < $3" + " AND" + " creation_time < $4" + " AND ($5 OR " + " NOT CAST($6 AS BOOL))" /* unclaimed orders are never paid */ + " AND ($7 OR " + " NOT CAST($8 AS BOOL))"/* unclaimed orders are never refunded */ + " AND ($9 OR " + " NOT CAST($10 AS BOOL))" /* unclaimed orders are never wired */ + " AND" + " order_serial NOT IN" + " (SELECT order_serial" + " FROM merchant_contract_terms)" /* only select unclaimed orders */ + " AND ($11 OR " + " ($12 = session_id))" + " AND ($13 OR " + " ($14 = fulfillment_url))" + " AND ( ($15::TEXT IS NULL) OR " + " (LOWER(contract_terms ->> 'summary') LIKE LOWER($15)) )" + " ORDER BY order_serial DESC" + " LIMIT $2)" + "UNION " /* union ensures elements are distinct! */ + "(SELECT" + " order_id" + ",order_serial" + ",creation_time" + " FROM merchant_contract_terms" + " WHERE merchant_contract_terms.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND" + " order_serial < $3" + " AND" + " creation_time < $4" + " AND ($5 OR " + " (CAST($6 AS BOOL) = paid))" + " AND ($7 OR " + " (CAST($8 AS BOOL) = (order_serial IN" + " (SELECT order_serial " + " FROM merchant_refunds))))" + " AND ($9 OR" + " (CAST($10 AS BOOL) = wired))" + " AND ($11 OR " + " ($12 = session_id))" + " AND ($13 OR " + " ($14 = fulfillment_url))" + " AND ( ($15::TEXT IS NULL) OR " + " (LOWER(contract_terms ->> 'summary') LIKE LOWER($15)) )" + " ORDER BY order_serial DESC" + " LIMIT $2)" + " ORDER BY order_serial DESC" + " LIMIT $2"); - PREPARE_INSTANCE (pg, - stmt_inc, - "lookup_orders_inc", - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - " FROM merchant_orders" - " WHERE order_serial > $2" - " AND" - " creation_time > $3" - " AND ($4 OR " - " NOT CAST($5 AS BOOL))" /* unclaimed orders are never paid */ - " AND ($6 OR " - " NOT CAST($7 AS BOOL))"/* unclaimed orders are never refunded */ - " AND ($8 OR " - " NOT CAST($9 AS BOOL))" /* unclaimed orders are never wired */ - " AND" - " (order_serial NOT IN" - " (SELECT order_serial" - " FROM merchant_contract_terms))" /* only select unclaimed orders */ - " AND ($10 OR " - " ($11 = session_id))" - " AND ($12 OR " - " ($13 = fulfillment_url))" - " AND ( ($14::TEXT IS NULL) OR " - " (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )" - " ORDER BY order_serial ASC" - " LIMIT $1)" - "UNION " /* union ensures elements are distinct! */ - "(SELECT" - " order_id" - ",order_serial" - ",creation_time" - " FROM merchant_contract_terms" - " WHERE order_serial > $2" - " AND" - " creation_time > $3" - " AND ($4 OR " - " (CAST($5 AS BOOL) = paid))" - " AND ($6 OR " - " (CAST($7 AS BOOL) = (order_serial IN" - " (SELECT order_serial " - " FROM merchant_refunds))))" - " AND ($8 OR" - " (CAST($9 AS BOOL) = wired))" - " AND ($10 OR " - " ($11 = session_id))" - " AND ($12 OR " - " ($13 = fulfillment_url))" - " AND ( ($14::TEXT IS NULL) OR " - " (LOWER(contract_terms ->> 'summary') LIKE LOWER($14)) )" - " ORDER BY order_serial ASC" - " LIMIT $1)" - " ORDER BY order_serial ASC" - " LIMIT $1"); + PREPARE (pg, + "lookup_orders_inc", + "(SELECT" + " order_id" + ",order_serial" + ",creation_time" + " FROM merchant_orders" + " WHERE merchant_orders.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND" + " order_serial > $3" + " AND" + " creation_time > $4" + " AND ($5 OR " + " NOT CAST($6 AS BOOL))" /* unclaimed orders are never paid */ + " AND ($7 OR " + " NOT CAST($8 AS BOOL))"/* unclaimed orders are never refunded */ + " AND ($9 OR " + " NOT CAST($10 AS BOOL))" /* unclaimed orders are never wired */ + " AND" + " (order_serial NOT IN" + " (SELECT order_serial" + " FROM merchant_contract_terms))" /* only select unclaimed orders */ + " AND ($11 OR " + " ($12 = session_id))" + " AND ($13 OR " + " ($14 = fulfillment_url))" + " AND ( ($15::TEXT IS NULL) OR " + " (LOWER(contract_terms ->> 'summary') LIKE LOWER($15)) )" + " ORDER BY order_serial ASC" + " LIMIT $2)" + "UNION " /* union ensures elements are distinct! */ + "(SELECT" + " order_id" + ",order_serial" + ",creation_time" + " FROM merchant_contract_terms" + " WHERE merchant_contract_terms.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND" + " order_serial > $3" + " AND" + " creation_time > $4" + " AND ($5 OR " + " (CAST($6 AS BOOL) = paid))" + " AND ($7 OR " + " (CAST($8 AS BOOL) = (order_serial IN" + " (SELECT order_serial " + " FROM merchant_refunds))))" + " AND ($9 OR" + " (CAST($10 AS BOOL) = wired))" + " AND ($11 OR " + " ($12 = session_id))" + " AND ($13 OR " + " ($14 = fulfillment_url))" + " AND ( ($15::TEXT IS NULL) OR " + " (LOWER(contract_terms ->> 'summary') LIKE LOWER($15)) )" + " ORDER BY order_serial ASC" + " LIMIT $2)" + " ORDER BY order_serial ASC" + " LIMIT $2"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - (of->delta > 0) - ? stmt_inc - : stmt_dec, + stmt, params, &lookup_orders_cb, &plc); diff --git a/src/backenddb/lookup_otp_devices.c b/src/backenddb/lookup_otp_devices.c @@ -106,24 +106,23 @@ TALER_MERCHANTDB_lookup_otp_devices ( .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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_otp_devices", - "SELECT" - " otp_id" - ",otp_description" - " FROM merchant_otp_devices"); + PREPARE (pg, + "lookup_otp_devices", + "SELECT" + " otp_id" + ",otp_description" + " FROM merchant_otp_devices" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_otp_devices", params, &lookup_otp_devices_cb, &tlc); diff --git a/src/backenddb/lookup_pending_deposits.c b/src/backenddb/lookup_pending_deposits.c @@ -82,25 +82,25 @@ lookup_deposits_cb (void *cls, struct TALER_Amount deposit_fee; struct TALER_CoinSpendPublicKeyP coin_pub; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("out_deposit_serial", + GNUNET_PQ_result_spec_uint64 ("deposit_serial", &deposit_serial), - GNUNET_PQ_result_spec_auto_from_type ("out_h_contract_terms", + GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms", &h_contract_terms), - GNUNET_PQ_result_spec_auto_from_type ("out_merchant_priv", + GNUNET_PQ_result_spec_auto_from_type ("merchant_priv", &merchant_priv), - GNUNET_PQ_result_spec_string ("out_merchant_id", + GNUNET_PQ_result_spec_string ("merchant_id", &instance_id), - GNUNET_PQ_result_spec_absolute_time ("out_wire_transfer_deadline", + GNUNET_PQ_result_spec_absolute_time ("wire_transfer_deadline", &wire_deadline), - GNUNET_PQ_result_spec_absolute_time ("out_retry_time", + GNUNET_PQ_result_spec_absolute_time ("retry_time", &retry_time), - GNUNET_PQ_result_spec_auto_from_type ("out_h_wire", + GNUNET_PQ_result_spec_auto_from_type ("h_wire", &h_wire), - TALER_PQ_result_spec_amount_with_currency ("out_amount_with_fee", + TALER_PQ_result_spec_amount_with_currency ("amount_with_fee", &amount_with_fee), - TALER_PQ_result_spec_amount_with_currency ("out_deposit_fee", + TALER_PQ_result_spec_amount_with_currency ("deposit_fee", &deposit_fee), - GNUNET_PQ_result_spec_auto_from_type ("out_coin_pub", + GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub), GNUNET_PQ_result_spec_end }; @@ -159,17 +159,35 @@ TALER_MERCHANTDB_lookup_pending_deposits ( PREPARE (pg, "lookup_pending_deposits", "SELECT" - " out_deposit_serial" - " ,out_h_contract_terms" - " ,out_merchant_priv" - " ,out_merchant_id" - " ,out_wire_transfer_deadline" - " ,out_retry_time" - " ,out_h_wire" - " ,out_amount_with_fee" - " ,out_deposit_fee" - " ,out_coin_pub" - " FROM merchant.lookup_pending_deposits($1, $2, $3, $4)"); + " md.deposit_serial" + ",mct.h_contract_terms" + ",mk.merchant_priv" + ",mi.merchant_id" + ",mdc.wire_transfer_deadline" + ",md.settlement_retry_time AS retry_time" + ",ma.h_wire" + ",md.amount_with_fee" + ",md.deposit_fee" + ",md.coin_pub" + " FROM merchant_deposits md" + " JOIN merchant_deposit_confirmations mdc" + " USING (deposit_confirmation_serial)" + " JOIN merchant_contract_terms mct" + " ON (mct.order_serial=mdc.order_serial)" + " JOIN merchant_accounts ma" + " ON (mdc.account_serial=ma.account_serial)" + " LEFT JOIN merchant_kyc kyc" + " ON (mdc.account_serial=kyc.account_serial)" + " JOIN merchant_instances mi" + " ON (mct.merchant_serial=mi.merchant_serial)" + " JOIN merchant_keys mk" + " ON (mi.merchant_serial=mk.merchant_serial)" + " WHERE (mdc.exchange_url=$1)" + " AND md.settlement_retry_needed" + " AND ($4 OR (md.settlement_retry_time < $2))" + " AND ( (kyc.kyc_ok IS NULL) OR kyc.kyc_ok)" + " ORDER BY md.settlement_retry_time ASC" + " LIMIT $3"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_pending_deposits", params, diff --git a/src/backenddb/lookup_pending_webhooks.c b/src/backenddb/lookup_pending_webhooks.c @@ -72,22 +72,22 @@ lookup_pending_webhooks_cb (void *cls, char *header = NULL; char *body = NULL; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("out_webhook_pending_serial", + GNUNET_PQ_result_spec_uint64 ("webhook_pending_serial", &webhook_pending_serial), - GNUNET_PQ_result_spec_absolute_time ("out_next_attempt", + GNUNET_PQ_result_spec_absolute_time ("next_attempt", &next_attempt), - GNUNET_PQ_result_spec_uint32 ("out_retries", + GNUNET_PQ_result_spec_uint32 ("retries", &retries), - GNUNET_PQ_result_spec_string ("out_url", + GNUNET_PQ_result_spec_string ("url", &url), - GNUNET_PQ_result_spec_string ("out_http_method", + GNUNET_PQ_result_spec_string ("http_method", &http_method), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("out_header", + GNUNET_PQ_result_spec_string ("header", &header), NULL), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("out_body", + GNUNET_PQ_result_spec_string ("body", &body), NULL), GNUNET_PQ_result_spec_end @@ -138,14 +138,17 @@ TALER_MERCHANTDB_lookup_pending_webhooks ( PREPARE (pg, "lookup_pending_webhooks", "SELECT" - " out_webhook_pending_serial" - " ,out_next_attempt" - " ,out_retries" - " ,out_url" - " ,out_http_method" - " ,out_header" - " ,out_body" - " FROM merchant.lookup_pending_webhooks($1)"); + " webhook_pending_serial" + ",next_attempt" + ",retries" + ",url" + ",http_method" + ",header" + ",body" + " FROM merchant_pending_webhooks" + " WHERE next_attempt <= $1" + " ORDER BY next_attempt ASC" + ); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_pending_webhooks", @@ -179,14 +182,16 @@ TALER_MERCHANTDB_lookup_future_webhook (struct TALER_MERCHANTDB_PostgresContext PREPARE (pg, "lookup_future_webhook", "SELECT" - " out_webhook_pending_serial" - " ,out_next_attempt" - " ,out_retries" - " ,out_url" - " ,out_http_method" - " ,out_header" - " ,out_body" - " FROM merchant.lookup_future_webhook()"); + " webhook_pending_serial" + ",next_attempt" + ",retries" + ",url" + ",http_method" + ",header" + ",body" + " FROM merchant_pending_webhooks" + " ORDER BY next_attempt ASC LIMIT 1" + ); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "lookup_future_webhook", @@ -215,35 +220,35 @@ TALER_MERCHANTDB_lookup_all_webhooks (struct TALER_MERCHANTDB_PostgresContext *p }; 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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_all_webhooks", - " SELECT" - " webhook_pending_serial AS out_webhook_pending_serial" - " ,next_attempt AS out_next_attempt" - " ,retries AS out_retries" - " ,url AS out_url" - " ,http_method AS out_http_method" - " ,header AS out_header" - " ,body AS out_body" - " FROM merchant_pending_webhooks" - " WHERE webhook_pending_serial > $1" - " ORDER BY webhook_pending_serial" - " ASC LIMIT $2"); + PREPARE (pg, + "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"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_all_webhooks", params, &lookup_pending_webhooks_cb, &pwlc); diff --git a/src/backenddb/lookup_product.c b/src/backenddb/lookup_product.c @@ -35,50 +35,49 @@ TALER_MERCHANTDB_lookup_product (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t **categories) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (product_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); - PREPARE_INSTANCE (pg, - stmt, - "lookup_product", - "SELECT" - " mi.description" - ",mi.description_i18n::TEXT" - ",mi.product_name" - ",mi.unit" - ",mi.price_array" - ",mi.taxes::TEXT" - ",mi.total_stock" - ",mi.total_stock_frac" - ",mi.allow_fractional_quantity" - ",mi.fractional_precision_level" - ",mi.total_sold" - ",mi.total_sold_frac" - ",mi.total_lost" - ",mi.total_lost_frac" - ",mi.image" - ",mi.address::TEXT" - ",mi.next_restock" - ",mi.minimum_age" - ",mi.product_group_serial" - ",mi.money_pot_serial" - ",mi.price_is_net" - ",t.category_array AS categories" - " FROM merchant_inventory mi" - ",LATERAL (" - " SELECT ARRAY (" - " SELECT mpc.category_serial" - " FROM merchant_product_categories mpc" - " WHERE mpc.product_serial = mi.product_serial" - " ) AS category_array" - " ) t" - " WHERE mi.product_id=$1" - ); + PREPARE (pg, + "lookup_product", + "SELECT" + " mi.description" + ",mi.description_i18n::TEXT" + ",mi.product_name" + ",mi.unit" + ",mi.price_array" + ",mi.taxes::TEXT" + ",mi.total_stock" + ",mi.total_stock_frac" + ",mi.allow_fractional_quantity" + ",mi.fractional_precision_level" + ",mi.total_sold" + ",mi.total_sold_frac" + ",mi.total_lost" + ",mi.total_lost_frac" + ",mi.image" + ",mi.address::TEXT" + ",mi.next_restock" + ",mi.minimum_age" + ",mi.product_group_serial" + ",mi.money_pot_serial" + ",mi.price_is_net" + ",t.category_array AS categories" + " FROM merchant_inventory mi" + " JOIN merchant_instances inst" + " USING (merchant_serial)" + ",LATERAL (" + " SELECT ARRAY (" + " SELECT mpc.category_serial" + " FROM merchant_product_categories mpc" + " WHERE mpc.product_serial = mi.product_serial" + " ) AS category_array" + " ) t" + " WHERE inst.merchant_id=$1" + " AND mi.product_id=$2" + ); if (NULL == pd) { struct GNUNET_PQ_ResultSpec rs_null[] = { @@ -87,7 +86,7 @@ TALER_MERCHANTDB_lookup_product (struct TALER_MERCHANTDB_PostgresContext *pg, check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_product", params, rs_null); } @@ -164,7 +163,7 @@ TALER_MERCHANTDB_lookup_product (struct TALER_MERCHANTDB_PostgresContext *pg, pd->product_group_id = 0; pd->money_pot_id = 0; qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_product", params, rs); pd->product_name = my_name; diff --git a/src/backenddb/lookup_product_image.c b/src/backenddb/lookup_product_image.c @@ -33,23 +33,22 @@ TALER_MERCHANTDB_lookup_product_image_by_hash ( char **image) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (image_hash), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE ( + PREPARE ( pg, - stmt, "lookup_product_image_by_hash", "SELECT" " mi.image" " FROM merchant_inventory mi" - " WHERE mi.image_hash=$1"); + " JOIN merchant_instances inst" + " USING (merchant_serial)" + " WHERE inst.merchant_id=$1" + " AND mi.image_hash=$2"); *image = NULL; { @@ -61,7 +60,7 @@ TALER_MERCHANTDB_lookup_product_image_by_hash ( return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "lookup_product_image_by_hash", params, rs); } diff --git a/src/backenddb/lookup_products.c b/src/backenddb/lookup_products.c @@ -112,6 +112,7 @@ TALER_MERCHANTDB_lookup_products ( .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&offset), NULL == category_filter ? GNUNET_PQ_query_param_null () @@ -127,64 +128,65 @@ TALER_MERCHANTDB_lookup_products ( GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_asc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_asc, - "lookup_products_asc", - "SELECT" - " product_id" - " ,product_serial" - " FROM merchant_inventory" - " WHERE product_serial > $1" - " AND ( ($2::TEXT IS NULL) OR" - " (product_serial IN" - " (SELECT product_serial" - " FROM merchant_product_categories" - " WHERE category_serial IN" - " (SELECT category_serial" - " FROM merchant_categories" - " WHERE category_name LIKE LOWER($2)))) )" - " AND ( (0 = $6::INT8) OR" - " (product_group_serial = $6) )" - " AND ( ($3::TEXT IS NULL) OR" - " (product_name LIKE LOWER($3)) )" - " AND ( ($4::TEXT IS NULL) OR" - " (description LIKE LOWER($4)) )" - " ORDER BY product_serial ASC" - " LIMIT $5"); - PREPARE_INSTANCE (pg, - stmt_desc, - "lookup_products_desc", - "SELECT" - " product_id" - " ,product_serial" - " FROM merchant_inventory" - " WHERE product_serial < $1" - " AND ( ($2::TEXT IS NULL) OR" - " (product_serial IN" - " (SELECT product_serial" - " FROM merchant_product_categories" - " WHERE category_serial IN" - " (SELECT category_serial" - " FROM merchant_categories" - " WHERE category_name LIKE LOWER($2)))) )" - " AND ( (0 = $6::INT8) OR" - " (product_group_serial = $6) )" - " AND ( ($3::TEXT IS NULL) OR" - " (product_name LIKE LOWER($3)) )" - " AND ( ($4::TEXT IS NULL) OR" - " (description LIKE LOWER($4)) )" - " ORDER BY product_serial DESC" - " LIMIT $5"); + PREPARE (pg, + "lookup_products_asc", + "SELECT" + " product_id" + " ,product_serial" + " FROM merchant_inventory" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND product_serial > $2" + " AND ( ($3::TEXT IS NULL) OR" + " (product_serial IN" + " (SELECT product_serial" + " FROM merchant_product_categories" + " WHERE category_serial IN" + " (SELECT category_serial" + " FROM merchant_categories" + " WHERE category_name LIKE LOWER($3)))) )" + " AND ( (0 = $7::INT8) OR" + " (product_group_serial = $7) )" + " AND ( ($4::TEXT IS NULL) OR" + " (product_name LIKE LOWER($4)) )" + " AND ( ($5::TEXT IS NULL) OR" + " (description LIKE LOWER($5)) )" + " ORDER BY product_serial ASC" + " LIMIT $6"); + PREPARE (pg, + "lookup_products_desc", + "SELECT" + " product_id" + " ,product_serial" + " FROM merchant_inventory" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND product_serial < $2" + " AND ( ($3::TEXT IS NULL) OR" + " (product_serial IN" + " (SELECT product_serial" + " FROM merchant_product_categories" + " WHERE category_serial IN" + " (SELECT category_serial" + " FROM merchant_categories" + " WHERE category_name LIKE LOWER($3)))) )" + " AND ( (0 = $7::INT8) OR" + " (product_group_serial = $7) )" + " AND ( ($4::TEXT IS NULL) OR" + " (product_name LIKE LOWER($4)) )" + " AND ( ($5::TEXT IS NULL) OR" + " (description LIKE LOWER($5)) )" + " ORDER BY product_serial DESC" + " LIMIT $6"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - (limit > 0) ? stmt_asc : stmt_desc, + (limit > 0) + ? "lookup_products_asc" + : "lookup_products_desc", params, &lookup_products_cb, &plc); diff --git a/src/backenddb/lookup_reconciliation_details.c b/src/backenddb/lookup_reconciliation_details.c @@ -139,37 +139,36 @@ TALER_MERCHANTDB_lookup_reconciliation_details ( .pg = pg }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&expected_incoming_serial), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_reconciliation_details", - "SELECT" - " mct.order_id" - ",SUM((etc.exchange_deposit_value).val)::INT8 AS deposit_value_sum" - ",SUM((etc.exchange_deposit_value).frac)::INT8 AS deposit_frac_sum" - ",SUM((etc.exchange_deposit_fee).val)::INT8 AS fee_value_sum" - ",SUM((etc.exchange_deposit_fee).frac)::INT8 AS fee_frac_sum" - ",(etc.exchange_deposit_value).curr AS currency" - " FROM merchant_expected_transfer_to_coin etc" - " JOIN merchant_deposits md" - " USING (deposit_serial)" - " JOIN merchant_deposit_confirmations mdc" - " USING (deposit_confirmation_serial)" - " JOIN merchant_contract_terms mct" - " USING (order_serial)" - " WHERE expected_credit_serial=$1" - " GROUP BY mct.order_id, (etc.exchange_deposit_value).curr;"); + PREPARE (pg, + "lookup_reconciliation_details", + "SELECT" + " mct.order_id" + ",SUM((etc.exchange_deposit_value).val)::INT8 AS deposit_value_sum" + ",SUM((etc.exchange_deposit_value).frac)::INT8 AS deposit_frac_sum" + ",SUM((etc.exchange_deposit_fee).val)::INT8 AS fee_value_sum" + ",SUM((etc.exchange_deposit_fee).frac)::INT8 AS fee_frac_sum" + ",(etc.exchange_deposit_value).curr AS currency" + " FROM merchant_expected_transfer_to_coin etc" + " JOIN merchant_deposits md" + " USING (deposit_serial)" + " JOIN merchant_deposit_confirmations mdc" + " USING (deposit_confirmation_serial)" + " JOIN merchant_contract_terms mct" + " USING (order_serial)" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE expected_credit_serial=$2" + " AND mi.merchant_id=$1" + " GROUP BY mct.order_id, (etc.exchange_deposit_value).curr;"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_reconciliation_details", params, &reconciliation_cb, &lic); diff --git a/src/backenddb/lookup_refund_proof.c b/src/backenddb/lookup_refund_proof.c @@ -43,24 +43,20 @@ TALER_MERCHANTDB_lookup_refund_proof (struct TALER_MERCHANTDB_PostgresContext *p GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_refund_proof", - "SELECT" - " merchant.merchant_exchange_signing_keys.exchange_pub" - ",exchange_sig" - " FROM merchant_refund_proofs" - " JOIN merchant.merchant_exchange_signing_keys" - " USING (signkey_serial)" - " WHERE" - " refund_serial=$1"); + PREPARE (pg, + "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"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_refund_proof", params, rs); } diff --git a/src/backenddb/lookup_refunds.c b/src/backenddb/lookup_refunds.c @@ -105,6 +105,7 @@ TALER_MERCHANTDB_lookup_refunds (struct TALER_MERCHANTDB_PostgresContext *pg, void *rc_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 }; @@ -114,29 +115,28 @@ TALER_MERCHANTDB_lookup_refunds (struct TALER_MERCHANTDB_PostgresContext *pg, .pg = pg }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); /* no preflight check here, run in transaction by caller! */ TALER_LOG_DEBUG ("Looking for refund of h_contract_terms %s at `%s'\n", GNUNET_h2s (&h_contract_terms->hash), instance_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_refunds", - "SELECT" - " coin_pub" - ",refund_amount" - " FROM merchant_refunds" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$1)"); + PREPARE (pg, + "lookup_refunds", + "SELECT" + " coin_pub" + ",refund_amount" + " FROM merchant_refunds" + " 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))"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_refunds", params, &lookup_refunds_cb, &lrc); diff --git a/src/backenddb/lookup_refunds_detailed.c b/src/backenddb/lookup_refunds_detailed.c @@ -131,6 +131,7 @@ TALER_MERCHANTDB_lookup_refunds_detailed ( void *rc_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 }; @@ -140,43 +141,42 @@ TALER_MERCHANTDB_lookup_refunds_detailed ( .pg = pg }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); /* 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); - PREPARE_INSTANCE (pg, - stmt, - "lookup_refunds_detailed", - "SELECT" - " ref.refund_serial" - ",ref.refund_timestamp" - ",dep.coin_pub" - ",mcon.exchange_url" - ",ref.rtransaction_id" - ",ref.reason" - ",ref.refund_amount" - ",merchant_refund_proofs.exchange_sig IS NULL AS pending" - " FROM merchant_deposit_confirmations mcon" - " JOIN merchant_deposits dep" - " USING (deposit_confirmation_serial)" - " JOIN merchant_refunds ref" - " USING (order_serial, coin_pub)" - " LEFT JOIN merchant_refund_proofs" - " USING (refund_serial)" - " WHERE mcon.order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$1)"); + PREPARE (pg, + "lookup_refunds_detailed", + "SELECT" + " ref.refund_serial" + ",ref.refund_timestamp" + ",dep.coin_pub" + ",mcon.exchange_url" + ",ref.rtransaction_id" + ",ref.reason" + ",ref.refund_amount" + ",merchant_refund_proofs.exchange_sig IS NULL AS pending" + " FROM merchant_deposit_confirmations mcon" + " JOIN merchant_deposits dep" + " USING (deposit_confirmation_serial)" + " JOIN merchant_refunds ref" + " USING (order_serial, coin_pub)" + " LEFT JOIN merchant_refund_proofs" + " USING (refund_serial)" + " WHERE mcon.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))"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_refunds_detailed", params, &lookup_refunds_detailed_cb, &lrdc); diff --git a/src/backenddb/lookup_reports_pending.c b/src/backenddb/lookup_reports_pending.c @@ -77,27 +77,27 @@ select_pending_reports_cb (void *cls, struct GNUNET_TIME_Absolute next_transmission; bool one_shot; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("out_merchant_id", + GNUNET_PQ_result_spec_string ("merchant_id", &instance_id), - GNUNET_PQ_result_spec_uint64 ("out_report_serial", + GNUNET_PQ_result_spec_uint64 ("report_serial", &report_serial), - GNUNET_PQ_result_spec_string ("out_report_program_section", + GNUNET_PQ_result_spec_string ("report_program_section", &report_program_section), - GNUNET_PQ_result_spec_string ("out_report_description", + GNUNET_PQ_result_spec_string ("report_description", &report_description), - GNUNET_PQ_result_spec_string ("out_mime_type", + GNUNET_PQ_result_spec_string ("mime_type", &mime_type), - GNUNET_PQ_result_spec_auto_from_type ("out_report_token", + GNUNET_PQ_result_spec_auto_from_type ("report_token", &report_token), - GNUNET_PQ_result_spec_string ("out_target_address", + GNUNET_PQ_result_spec_string ("target_address", &target_address), - GNUNET_PQ_result_spec_relative_time ("out_frequency", + GNUNET_PQ_result_spec_relative_time ("frequency", &frequency), - GNUNET_PQ_result_spec_relative_time ("out_frequency_shift", + GNUNET_PQ_result_spec_relative_time ("frequency_shift", &frequency_shift), - GNUNET_PQ_result_spec_absolute_time ("out_next_transmission", + GNUNET_PQ_result_spec_absolute_time ("next_transmission", &next_transmission), - GNUNET_PQ_result_spec_bool ("out_one_shot_hidden", + GNUNET_PQ_result_spec_bool ("one_shot_hidden", &one_shot), GNUNET_PQ_result_spec_end }; @@ -149,18 +149,22 @@ TALER_MERCHANTDB_lookup_reports_pending ( PREPARE (pg, "lookup_reports_pending", "SELECT" - " out_merchant_id" - " ,out_report_serial" - " ,out_report_program_section" - " ,out_report_description" - " ,out_mime_type" - " ,out_report_token" - " ,out_target_address" - " ,out_frequency" - " ,out_frequency_shift" - " ,out_next_transmission" - " ,out_one_shot_hidden" - " FROM merchant.lookup_reports_pending()"); + " mi.merchant_id" + " ,mr.report_serial" + " ,mr.report_program_section" + " ,mr.report_description" + " ,mr.mime_type" + " ,mr.report_token" + " ,mr.target_address" + " ,mr.frequency" + " ,mr.frequency_shift" + " ,mr.next_transmission" + " ,mr.one_shot_hidden" + " FROM merchant_reports mr" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " ORDER BY next_transmission ASC" + " LIMIT 1;"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, "lookup_reports_pending", diff --git a/src/backenddb/lookup_spent_tokens_by_order.c b/src/backenddb/lookup_spent_tokens_by_order.c @@ -133,29 +133,25 @@ TALER_MERCHANTDB_lookup_spent_tokens_by_order ( }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_spent_tokens_by_order", - "SELECT" - " spent_token_serial" - ",h_contract_terms" - ",h_pub" - ",token_pub" - ",token_sig" - ",blind_sig" - " FROM merchant_used_tokens" - " JOIN merchant_contract_terms" - " USING (h_contract_terms)" - " JOIN merchant_token_family_keys" - " USING (token_family_key_serial)" - " WHERE order_serial=$1"); + PREPARE (pg, + "lookup_spent_tokens_by_order", + "SELECT" + " spent_token_serial" + ",h_contract_terms" + ",h_pub" + ",token_pub" + ",token_sig" + ",blind_sig" + " FROM merchant_used_tokens" + " JOIN merchant_contract_terms" + " USING (h_contract_terms)" + " JOIN merchant_token_family_keys" + " USING (token_family_key_serial)" + " WHERE order_serial=$1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_spent_tokens_by_order", params, &lookup_spent_tokens_by_order_cb, &ctx); diff --git a/src/backenddb/lookup_statistics_amount_by_bucket.c b/src/backenddb/lookup_statistics_amount_by_bucket.c @@ -186,34 +186,33 @@ TALER_MERCHANTDB_lookup_statistics_amount_by_bucket ( .pg = pg, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (slug), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_statistics_amount_by_bucket", - "SELECT" - " bmeta_serial_id" - ",description" - ",bucket_start" - ",bucket_range::TEXT" - ",merchant_statistics_bucket_end(bucket_start, bucket_range) AS bucket_end" - ",(cumulative_value,cumulative_frac,curr)::taler_amount_currency AS cumulative_amount" - " FROM merchant_statistic_bucket_amount" - " JOIN merchant_statistic_bucket_meta" - " USING (bmeta_serial_id)" - " WHERE merchant_statistic_bucket_meta.slug=$1" - " AND merchant_statistic_bucket_meta.stype='amount'"); + PREPARE (pg, + "lookup_statistics_amount_by_bucket", + "SELECT" + " bmeta_serial_id" + ",description" + ",bucket_start" + ",bucket_range::TEXT" + ",merchant_statistics_bucket_end(bucket_start, bucket_range) AS bucket_end" + ",(cumulative_value,cumulative_frac,curr)::taler_amount_currency AS cumulative_amount" + " FROM merchant_statistic_bucket_amount" + " JOIN merchant_statistic_bucket_meta" + " USING (bmeta_serial_id)" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND merchant_statistic_bucket_meta.slug=$2" + " AND merchant_statistic_bucket_meta.stype='amount'"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_statistics_amount_by_bucket", params, &lookup_statistics_amount_by_bucket_cb, &context); diff --git a/src/backenddb/lookup_statistics_amount_by_bucket2.c b/src/backenddb/lookup_statistics_amount_by_bucket2.c @@ -123,38 +123,37 @@ TALER_MERCHANTDB_lookup_statistics_amount_by_bucket2 ( .pg = pg, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (slug), GNUNET_PQ_query_param_string (granularity), GNUNET_PQ_query_param_uint64 (&counter), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_statistics_amount_by_bucket2", - "SELECT" - " msba.bucket_start" - ",ARRAY_AGG (" - " ROW(msba.cumulative_value::INT8, msba.cumulative_frac::INT4, msba.curr)::taler_amount_currency" - " ) AS cumulative_amounts" - " FROM merchant_statistic_bucket_meta msbm" - " JOIN merchant_statistic_bucket_amount msba" - " USING (bmeta_serial_id)" - " WHERE msbm.slug=$1" - " AND msba.bucket_range=$2::TEXT::statistic_range" - " AND msbm.stype='amount'" - " GROUP BY msba.bucket_start" - " ORDER BY msba.bucket_start DESC" - " LIMIT $3;"); + PREPARE (pg, + "lookup_statistics_amount_by_bucket2", + "SELECT" + " msba.bucket_start" + ",ARRAY_AGG (" + " ROW(msba.cumulative_value::INT8, msba.cumulative_frac::INT4, msba.curr)::taler_amount_currency" + " ) AS cumulative_amounts" + " FROM merchant_statistic_bucket_meta msbm" + " JOIN merchant_statistic_bucket_amount msba" + " USING (bmeta_serial_id)" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mi.merchant_id=$1" + " AND msbm.slug=$2" + " AND msba.bucket_range=$3::TEXT::statistic_range" + " AND msbm.stype='amount'" + " GROUP BY msba.bucket_start" + " ORDER BY msba.bucket_start DESC" + " LIMIT $4;"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_statistics_amount_by_bucket2", params, &lookup_statistics_amount_by_bucket_cb2, &context); diff --git a/src/backenddb/lookup_statistics_amount_by_interval.c b/src/backenddb/lookup_statistics_amount_by_interval.c @@ -198,26 +198,21 @@ TALER_MERCHANTDB_lookup_statistics_amount_by_interval ( GNUNET_PQ_query_param_end }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (slug), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_desc, - "lookup_statistics_amount_by_interval_description", - "SELECT description" - " FROM merchant_statistic_interval_meta" - " WHERE slug=$1 LIMIT 1"); + PREPARE (pg, + "lookup_statistics_amount_by_interval_description", + "SELECT description" + " FROM merchant_statistic_interval_meta" + " WHERE slug=$1 LIMIT 1"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt_desc, + "lookup_statistics_amount_by_interval_description", descParams, &lookup_statistics_amount_by_interval_desc_cb, &context); @@ -227,14 +222,13 @@ TALER_MERCHANTDB_lookup_statistics_amount_by_interval ( GNUNET_break (0); return GNUNET_DB_STATUS_HARD_ERROR; } - PREPARE_INSTANCE (pg, - stmt, - "lookup_statistics_amount_by_interval", - "SELECT *" - " FROM merchant_statistic_interval_amount_get($1)"); + PREPARE (pg, + "lookup_statistics_amount_by_interval", + "SELECT *" + " FROM merchant_statistic_interval_amount_get($2,$1)"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_statistics_amount_by_interval", params, &lookup_statistics_amount_by_interval_cb, &context); diff --git a/src/backenddb/lookup_statistics_counter_by_bucket.c b/src/backenddb/lookup_statistics_counter_by_bucket.c @@ -125,33 +125,32 @@ TALER_MERCHANTDB_lookup_statistics_counter_by_bucket ( .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (slug), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_statistics_counter_by_bucket", - "SELECT" - " description" - ",bucket_start" - ",bucket_range::TEXT" - ",merchant_statistics_bucket_end(bucket_start, bucket_range) AS bucket_end" - ",cumulative_number" - " FROM merchant_statistic_bucket_counter" - " JOIN merchant_statistic_bucket_meta" - " USING (bmeta_serial_id)" - " WHERE merchant_statistic_bucket_meta.slug=$1" - " AND merchant_statistic_bucket_meta.stype = 'number'"); + PREPARE (pg, + "lookup_statistics_counter_by_bucket", + "SELECT" + " description" + ",bucket_start" + ",bucket_range::TEXT" + ",merchant_statistics_bucket_end(bucket_start, bucket_range) AS bucket_end" + ",cumulative_number" + " FROM merchant_statistic_bucket_counter" + " JOIN merchant_statistic_bucket_meta" + " USING (bmeta_serial_id)" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND merchant_statistic_bucket_meta.slug=$2" + " AND merchant_statistic_bucket_meta.stype = 'number'"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_statistics_counter_by_bucket", params, &lookup_statistics_counter_by_bucket_cb, &context); diff --git a/src/backenddb/lookup_statistics_counter_by_bucket2.c b/src/backenddb/lookup_statistics_counter_by_bucket2.c @@ -147,37 +147,36 @@ TALER_MERCHANTDB_lookup_statistics_counter_by_bucket2 ( .pg = pg }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (prefix), GNUNET_PQ_query_param_string (granularity), GNUNET_PQ_query_param_uint64 (&counter), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_statistics_counter_by_bucket2", - "SELECT" - " msbc.bucket_start" - ",ARRAY_AGG(msbm.slug) AS slugs" - ",ARRAY_AGG(msbc.cumulative_number) AS counters" - " FROM merchant_statistic_bucket_counter msbc" - " JOIN merchant_statistic_bucket_meta msbm" - " USING (bmeta_serial_id)" - " WHERE msbm.slug LIKE $1" - " AND msbc.bucket_range=$2::TEXT::statistic_range" - " AND msbm.stype = 'number'" - " GROUP BY msbc.bucket_start" - " ORDER BY msbc.bucket_start DESC" - " LIMIT $3"); + PREPARE (pg, + "lookup_statistics_counter_by_bucket2", + "SELECT" + " msbc.bucket_start" + ",ARRAY_AGG(msbm.slug) AS slugs" + ",ARRAY_AGG(msbc.cumulative_number) AS counters" + " FROM merchant_statistic_bucket_counter msbc" + " JOIN merchant_statistic_bucket_meta msbm" + " USING (bmeta_serial_id)" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mi.merchant_id=$1" + " AND msbm.slug LIKE $2" + " AND msbc.bucket_range=$3::TEXT::statistic_range" + " AND msbm.stype = 'number'" + " GROUP BY msbc.bucket_start" + " ORDER BY msbc.bucket_start DESC" + " LIMIT $4"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_statistics_counter_by_bucket2", params, &lookup_statistics_counter_by_bucket_cb2, &context); diff --git a/src/backenddb/lookup_statistics_counter_by_interval.c b/src/backenddb/lookup_statistics_counter_by_interval.c @@ -164,26 +164,21 @@ TALER_MERCHANTDB_lookup_statistics_counter_by_interval ( GNUNET_PQ_query_param_end }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (slug), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_desc, - "lookup_statistics_counter_by_interval_description", - "SELECT description" - " FROM merchant_statistic_interval_meta" - " WHERE slug=$1 LIMIT 1"); + PREPARE (pg, + "lookup_statistics_counter_by_interval_description", + "SELECT description" + " FROM merchant_statistic_interval_meta" + " WHERE slug=$1 LIMIT 1"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt_desc, + "lookup_statistics_counter_by_interval_description", descParams, &lookup_statistics_counter_by_interval_desc_cb, &context); @@ -193,14 +188,13 @@ TALER_MERCHANTDB_lookup_statistics_counter_by_interval ( GNUNET_break (0); return GNUNET_DB_STATUS_HARD_ERROR; } - PREPARE_INSTANCE (pg, - stmt, - "lookup_statistics_counter_by_interval", - "SELECT *" - " FROM merchant_statistic_interval_number_get($1)"); + PREPARE (pg, + "lookup_statistics_counter_by_interval", + "SELECT *" + " FROM merchant_statistic_interval_number_get($2,$1)"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_statistics_counter_by_interval", params, &lookup_statistics_counter_by_interval_cb, &context); diff --git a/src/backenddb/lookup_template.c b/src/backenddb/lookup_template.c @@ -43,28 +43,27 @@ TALER_MERCHANTDB_lookup_template (struct TALER_MERCHANTDB_PostgresContext *pg, struct TALER_MERCHANTDB_TemplateDetails *td) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (template_id), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_template", - "SELECT" - " mt.template_description" - ",mod.otp_id" - ",mt.template_contract::TEXT" - ",mt.editable_defaults::TEXT" - " FROM merchant_template mt" - " LEFT JOIN merchant_otp_devices mod" - " ON (mod.otp_serial = mt.otp_device_id)" - " WHERE mt.template_id=$1"); + PREPARE (pg, + "lookup_template", + "SELECT" + " mt.template_description" + ",mod.otp_id" + ",mt.template_contract::TEXT" + ",mt.editable_defaults::TEXT" + " FROM merchant_template mt" + " JOIN merchant_instances mi" + " ON (mi.merchant_serial = mt.merchant_serial)" + " LEFT JOIN merchant_otp_devices mod" + " ON (mod.otp_serial = mt.otp_device_id)" + " WHERE mi.merchant_id=$1" + " AND mt.template_id=$2"); if (NULL == td) { struct GNUNET_PQ_ResultSpec rs_null[] = { @@ -72,7 +71,7 @@ TALER_MERCHANTDB_lookup_template (struct TALER_MERCHANTDB_PostgresContext *pg, }; qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_template", params, rs_null); GNUNET_PQ_cleanup_query_params_closures (params); @@ -100,7 +99,7 @@ TALER_MERCHANTDB_lookup_template (struct TALER_MERCHANTDB_PostgresContext *pg, 0, sizeof (*td)); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_template", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/lookup_templates.c b/src/backenddb/lookup_templates.c @@ -105,24 +105,23 @@ TALER_MERCHANTDB_lookup_templates (struct TALER_MERCHANTDB_PostgresContext *pg, .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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_templates", - "SELECT" - " template_id" - ",template_description" - " FROM merchant_template"); + PREPARE (pg, + "lookup_templates", + "SELECT" + " template_id" + ",template_description" + " FROM merchant_template" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_templates", params, &lookup_templates_cb, &tlc); diff --git a/src/backenddb/lookup_token_families.c b/src/backenddb/lookup_token_families.c @@ -130,29 +130,28 @@ TALER_MERCHANTDB_lookup_token_families ( .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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_token_families", - "SELECT" - " slug" - ",name" - ",description" - ",description_i18n::TEXT" - ",valid_after" - ",valid_before" - ",kind" - " FROM merchant_token_families"); + PREPARE (pg, + "lookup_token_families", + "SELECT" + " slug" + ",name" + ",description" + ",description_i18n::TEXT" + ",valid_after" + ",valid_before" + ",kind" + " FROM merchant_token_families" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_token_families", params, &lookup_token_families_cb, &context); diff --git a/src/backenddb/lookup_token_family.c b/src/backenddb/lookup_token_family.c @@ -33,43 +33,20 @@ TALER_MERCHANTDB_lookup_token_family (struct TALER_MERCHANTDB_PostgresContext *p struct TALER_MERCHANTDB_TokenFamilyDetails *details) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (token_family_slug), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); - check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_token_family", - "SELECT" - " slug" - ",name" - ",cipher_choice" - ",description" - ",description_i18n::TEXT" - ",extra_data::TEXT" - ",valid_after" - ",valid_before" - ",duration" - ",validity_granularity" - ",start_offset" - ",kind" - ",issued" - ",used" - " FROM merchant_token_families" - " WHERE slug=$1"); if (NULL == details) { struct GNUNET_PQ_ResultSpec rs_null[] = { GNUNET_PQ_result_spec_end }; + check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_token_family", params, rs_null); } @@ -111,11 +88,34 @@ TALER_MERCHANTDB_lookup_token_family (struct TALER_MERCHANTDB_PostgresContext *p }; enum GNUNET_DB_QueryStatus qs; + check_connection (pg); + PREPARE (pg, + "lookup_token_family", + "SELECT" + " slug" + ",name" + ",cipher_choice" + ",description" + ",description_i18n::TEXT" + ",extra_data::TEXT" + ",valid_after" + ",valid_before" + ",duration" + ",validity_granularity" + ",start_offset" + ",kind" + ",issued" + ",used" + " FROM merchant_token_families" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND merchant_token_families.slug=$2"); memset (details, 0, sizeof (*details)); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_token_family", params, rs); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) diff --git a/src/backenddb/lookup_token_family_key.c b/src/backenddb/lookup_token_family_key.c @@ -39,49 +39,48 @@ TALER_MERCHANTDB_lookup_token_family_key ( struct TALER_MERCHANTDB_TokenFamilyKeyDetails *details) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (token_family_slug), GNUNET_PQ_query_param_timestamp (&valid_at), GNUNET_PQ_query_param_timestamp (&sign_until), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_token_family_key", - "SELECT" - " h_pub" - ",pub" - ",priv" - ",cipher_choice" - ",mtfk.signature_validity_start" - ",mtfk.signature_validity_end" - ",mtfk.private_key_deleted_at" - ",slug" - ",name" - ",description" - ",description_i18n::TEXT" - ",mtf.valid_after" - ",mtf.valid_before" - ",duration" - ",validity_granularity" - ",start_offset" - ",kind" - ",issued" - ",used" - " FROM merchant_token_families mtf" - " LEFT JOIN merchant_token_family_keys mtfk" - " USING (token_family_serial)" - " WHERE slug=$1" - " AND COALESCE ($2 >= mtfk.signature_validity_start, TRUE)" - " AND COALESCE ($2 <= mtfk.signature_validity_end, TRUE)" - " AND COALESCE ($3 <= mtfk.private_key_deleted_at, TRUE)" - " ORDER BY mtfk.signature_validity_start ASC" - " LIMIT 1"); + PREPARE (pg, + "lookup_token_family_key", + "SELECT" + " h_pub" + ",pub" + ",priv" + ",cipher_choice" + ",mtfk.signature_validity_start" + ",mtfk.signature_validity_end" + ",mtfk.private_key_deleted_at" + ",slug" + ",name" + ",description" + ",description_i18n::TEXT" + ",mtf.valid_after" + ",mtf.valid_before" + ",duration" + ",validity_granularity" + ",start_offset" + ",kind" + ",issued" + ",used" + " FROM merchant_token_families mtf" + " LEFT JOIN merchant_token_family_keys mtfk" + " USING (token_family_serial)" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mi.merchant_id=$1" + " AND slug=$2" + " AND COALESCE ($3 >= mtfk.signature_validity_start, TRUE)" + " AND COALESCE ($3 <= mtfk.signature_validity_end, TRUE)" + " AND COALESCE ($4 <= mtfk.private_key_deleted_at, TRUE)" + " ORDER BY mtfk.signature_validity_start ASC" + " LIMIT 1"); if (NULL == details) { @@ -90,7 +89,7 @@ TALER_MERCHANTDB_lookup_token_family_key ( }; return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_token_family_key", params, rs_null); } @@ -153,7 +152,7 @@ TALER_MERCHANTDB_lookup_token_family_key ( 0, sizeof (*details)); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_token_family_key", params, rs); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) diff --git a/src/backenddb/lookup_token_family_keys.c b/src/backenddb/lookup_token_family_keys.c @@ -166,6 +166,7 @@ TALER_MERCHANTDB_lookup_token_family_keys ( void *cb_cls) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (token_family_slug), GNUNET_PQ_query_param_timestamp (&start_time), GNUNET_PQ_query_param_timestamp (&end_time), @@ -177,44 +178,42 @@ TALER_MERCHANTDB_lookup_token_family_keys ( .cb_cls = cb_cls }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_token_family_keys", - "SELECT" - " h_pub" - ",mtfk.pub" - ",mtfk.priv" - ",cipher_choice" - ",mtfk.signature_validity_start" - ",mtfk.signature_validity_end" - ",mtfk.private_key_deleted_at" - ",slug" - ",name" - ",description" - ",description_i18n::TEXT" - ",mtf.valid_after" - ",mtf.valid_before" - ",duration" - ",validity_granularity" - ",start_offset" - ",kind" - ",issued" - ",used" - " FROM merchant_token_families mtf" - " LEFT JOIN merchant_token_family_keys mtfk" - " USING (token_family_serial)" - " WHERE slug=$1" - " AND COALESCE ($2 <= mtfk.signature_validity_end, TRUE)" - " AND COALESCE ($3 >= mtfk.signature_validity_start, TRUE);"); + PREPARE (pg, + "lookup_token_family_keys", + "SELECT" + " h_pub" + ",mtfk.pub" + ",mtfk.priv" + ",cipher_choice" + ",mtfk.signature_validity_start" + ",mtfk.signature_validity_end" + ",mtfk.private_key_deleted_at" + ",slug" + ",name" + ",description" + ",description_i18n::TEXT" + ",mtf.valid_after" + ",mtf.valid_before" + ",duration" + ",validity_granularity" + ",start_offset" + ",kind" + ",issued" + ",used" + " FROM merchant_token_families mtf" + " LEFT JOIN merchant_token_family_keys mtfk" + " USING (token_family_serial)" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mi.merchant_id=$1" + " AND slug=$2" + " AND COALESCE ($3 <= mtfk.signature_validity_end, TRUE)" + " AND COALESCE ($4 >= mtfk.signature_validity_start, TRUE);"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_token_family_keys", params, &lookup_token_keys_cb, &ctx); diff --git a/src/backenddb/lookup_transfer_details.c b/src/backenddb/lookup_transfer_details.c @@ -123,34 +123,30 @@ TALER_MERCHANTDB_lookup_transfer_details ( }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_transfer_details", - "SELECT" - " mterm.h_contract_terms" - ",mtcoin.offset_in_exchange_list" - ",dep.coin_pub" - ",mtcoin.exchange_deposit_value" - ",mtcoin.exchange_deposit_fee" - " FROM merchant_expected_transfer_to_coin mtcoin" - " JOIN merchant_deposits dep" - " USING (deposit_serial)" - " JOIN merchant_deposit_confirmations mcon" - " USING (deposit_confirmation_serial)" - " JOIN merchant_contract_terms mterm" - " USING (order_serial)" - " JOIN merchant_expected_transfers met" - " USING (expected_credit_serial)" - " WHERE met.wtid=$2" - " AND met.exchange_url=$1"); + PREPARE (pg, + "lookup_transfer_details", + "SELECT" + " mterm.h_contract_terms" + ",mtcoin.offset_in_exchange_list" + ",dep.coin_pub" + ",mtcoin.exchange_deposit_value" + ",mtcoin.exchange_deposit_fee" + " FROM merchant_expected_transfer_to_coin mtcoin" + " JOIN merchant_deposits dep" + " USING (deposit_serial)" + " JOIN merchant_deposit_confirmations mcon" + " USING (deposit_confirmation_serial)" + " JOIN merchant_contract_terms mterm" + " USING (order_serial)" + " JOIN merchant_expected_transfers met" + " USING (expected_credit_serial)" + " WHERE met.wtid=$2" + " AND met.exchange_url=$1"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_transfer_details", params, &lookup_transfer_details_cb, &ltdc); diff --git a/src/backenddb/lookup_transfer_details_by_order.c b/src/backenddb/lookup_transfer_details_by_order.c @@ -201,39 +201,36 @@ TALER_MERCHANTDB_lookup_transfer_details_by_order ( }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_transfer_details_by_order", - "SELECT" - " md.deposit_serial" - ",mcon.exchange_url" - ",met.wtid" - ",mtc.exchange_deposit_value" - ",mtc.exchange_deposit_fee" - ",mcon.deposit_timestamp" - ",met.confirmed" - ",met.expected_credit_serial" - " FROM merchant_expected_transfer_to_coin mtc" - " JOIN merchant_deposits md" - " USING (deposit_serial)" - " JOIN merchant_deposit_confirmations mcon" - " USING (deposit_confirmation_serial)" - " JOIN merchant_expected_transfers met" - " USING (expected_credit_serial)" - " JOIN merchant_accounts acc" - " ON (acc.account_serial = met.account_serial)" - " JOIN merchant_contract_terms contracts" - " USING (order_serial)" - " WHERE mcon.order_serial=$1" - " ORDER BY met.wtid"); + PREPARE (pg, + "lookup_transfer_details_by_order", + "SELECT" + " md.deposit_serial" + ",mcon.exchange_url" + ",met.wtid" + ",mtc.exchange_deposit_value" + ",mtc.exchange_deposit_fee" + ",mcon.deposit_timestamp" + ",met.confirmed" + ",met.expected_credit_serial" + " FROM merchant_expected_transfer_to_coin mtc" + " JOIN merchant_deposits md" + " USING (deposit_serial)" + " JOIN merchant_deposit_confirmations mcon" + " USING (deposit_confirmation_serial)" + " JOIN merchant_expected_transfers met" + " USING (expected_credit_serial)" + " JOIN merchant_accounts acc" + " ON (acc.account_serial = met.account_serial)" + /* Check that all this is for the same instance */ + " JOIN merchant_contract_terms contracts" + " USING (merchant_serial, order_serial)" + " WHERE mcon.order_serial=$1" + " ORDER BY met.wtid"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_transfer_details_by_order", params, &lookup_transfer_details_by_order_cb, &ltdo); diff --git a/src/backenddb/lookup_transfer_summary.c b/src/backenddb/lookup_transfer_summary.c @@ -121,32 +121,28 @@ TALER_MERCHANTDB_lookup_transfer_summary ( }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_transfer_summary", - "SELECT" - " mct.order_id" - ",mtc.exchange_deposit_value" - ",mtc.exchange_deposit_fee" - " FROM merchant_expected_transfers met" - " JOIN merchant_expected_transfer_to_coin mtc" - " USING (expected_credit_serial)" - " JOIN merchant_deposits dep" - " USING (deposit_serial)" - " JOIN merchant_deposit_confirmations mcon" - " USING (deposit_confirmation_serial)" - " JOIN merchant_contract_terms mct" - " USING (order_serial)" - " WHERE met.wtid=$2" - " AND met.exchange_url=$1"); + PREPARE (pg, + "lookup_transfer_summary", + "SELECT" + " mct.order_id" + ",mtc.exchange_deposit_value" + ",mtc.exchange_deposit_fee" + " FROM merchant_expected_transfers met" + " JOIN merchant_expected_transfer_to_coin mtc" + " USING (expected_credit_serial)" + " JOIN merchant_deposits dep" + " USING (deposit_serial)" + " JOIN merchant_deposit_confirmations mcon" + " USING (deposit_confirmation_serial)" + " JOIN merchant_contract_terms mct" + " USING (order_serial)" + " WHERE met.wtid=$2" + " AND met.exchange_url=$1"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "lookup_transfer_summary", params, &lookup_transfer_summary_cb, &ltdc); diff --git a/src/backenddb/lookup_transfers.c b/src/backenddb/lookup_transfers.c @@ -147,6 +147,7 @@ TALER_MERCHANTDB_lookup_transfers ( .pg = pg }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_timestamp (&before), GNUNET_PQ_query_param_timestamp (&after), GNUNET_PQ_query_param_uint64 (&offset), @@ -154,85 +155,88 @@ TALER_MERCHANTDB_lookup_transfers ( NULL == payto_uri.full_payto ? GNUNET_PQ_query_param_null () /* NULL: do not filter by payto URI */ : GNUNET_PQ_query_param_string (payto_uri.full_payto), - GNUNET_PQ_query_param_bool (! by_time), /* $6: filter by time? */ + GNUNET_PQ_query_param_bool (! by_time), /* $7: filter by time? */ GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_ALL == expected), /* filter by expected? */ GNUNET_PQ_query_param_bool (TALER_EXCHANGE_YNA_YES == expected), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_asc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_asc, - "lookup_transfers_asc", - "SELECT" - " mt.credit_amount" - ",mt.wtid" - ",mac.payto_uri" - ",mt.exchange_url" - ",mt.credit_serial" - ",mt.execution_time" - ",mt.expected" - ",met.expected_credit_serial" - " FROM merchant_transfers mt" - " JOIN merchant_accounts mac" - " USING (account_serial)" - " LEFT JOIN merchant_expected_transfers met" - " ON mt.wtid = met.wtid" - " AND mt.account_serial = met.account_serial" - " AND mt.exchange_url = met.exchange_url" - " AND mt.expected" - " WHERE ( $6 OR " - " (mt.execution_time < $1 AND" - " mt.execution_time >= $2) )" - " AND ( (CAST($5 AS TEXT) IS NULL) OR " - " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($5,'\\?.*','')) )" - " AND ( $7 OR " - " (mt.expected = $8) )" - " AND (mt.credit_serial > $3)" - " ORDER BY mt.credit_serial ASC" - " LIMIT $4"); - PREPARE_INSTANCE (pg, - stmt_desc, - "lookup_transfers_desc", - "SELECT" - " mt.credit_amount" - ",mt.wtid" - ",mac.payto_uri" - ",mt.exchange_url" - ",mt.credit_serial" - ",mt.execution_time" - ",mt.expected" - ",met.expected_credit_serial" - " FROM merchant_transfers mt" - " JOIN merchant_accounts mac" - " USING (account_serial)" - " LEFT JOIN merchant_expected_transfers met" - " ON mt.wtid = met.wtid" - " AND mt.account_serial = met.account_serial" - " AND mt.exchange_url = met.exchange_url" - " AND mt.expected" - " WHERE ( $6 OR " - " (mt.execution_time < $1 AND" - " mt.execution_time >= $2) )" - " AND ( (CAST($5 AS TEXT) IS NULL) OR " - " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" - " =REGEXP_REPLACE($5,'\\?.*','')) )" - " AND ( $7 OR " - " (mt.expected = $8) )" - " AND (mt.credit_serial < $3)" - " ORDER BY mt.credit_serial DESC" - " LIMIT $4"); + PREPARE (pg, + "lookup_transfers_asc", + "SELECT" + " mt.credit_amount" + ",mt.wtid" + ",mac.payto_uri" + ",mt.exchange_url" + ",mt.credit_serial" + ",mt.execution_time" + ",mt.expected" + ",met.expected_credit_serial" + " FROM merchant_transfers mt" + " JOIN merchant_accounts mac" + " USING (account_serial)" + " LEFT JOIN merchant_expected_transfers met" + " ON mt.wtid = met.wtid" + " AND mt.account_serial = met.account_serial" + " AND mt.exchange_url = met.exchange_url" + " AND mt.expected" + " WHERE ( $7 OR " + " (mt.execution_time < $2 AND" + " mt.execution_time >= $3) )" + " AND ( (CAST($6 AS TEXT) IS NULL) OR " + " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" + " =REGEXP_REPLACE($6,'\\?.*','')) )" + " AND ( $8 OR " + " (mt.expected = $9) )" + " AND merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND (mt.credit_serial > $4)" + " ORDER BY mt.credit_serial ASC" + " LIMIT $5"); + PREPARE (pg, + "lookup_transfers_desc", + "SELECT" + " mt.credit_amount" + ",mt.wtid" + ",mac.payto_uri" + ",mt.exchange_url" + ",mt.credit_serial" + ",mt.execution_time" + ",mt.expected" + ",met.expected_credit_serial" + " FROM merchant_transfers mt" + " JOIN merchant_accounts mac" + " USING (account_serial)" + " LEFT JOIN merchant_expected_transfers met" + " ON mt.wtid = met.wtid" + " AND mt.account_serial = met.account_serial" + " AND mt.exchange_url = met.exchange_url" + " AND mt.expected" + " WHERE ( $7 OR " + " (mt.execution_time < $2 AND" + " mt.execution_time >= $3) )" + " AND ( (CAST($6 AS TEXT) IS NULL) OR " + " (REGEXP_REPLACE(mac.payto_uri,'\\?.*','')" + " =REGEXP_REPLACE($6,'\\?.*','')) )" + " AND ( $8 OR " + " (mt.expected = $9) )" + " AND merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND (mt.credit_serial < $4)" + " ORDER BY mt.credit_serial DESC" + " LIMIT $5"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - (limit > 0) ? stmt_asc : stmt_desc, + (limit > 0) + ? "lookup_transfers_asc" + : "lookup_transfers_desc", params, &lookup_transfers_cb, &ltc); diff --git a/src/backenddb/lookup_units.c b/src/backenddb/lookup_units.c @@ -100,46 +100,48 @@ TALER_MERCHANTDB_lookup_units (struct TALER_MERCHANTDB_PostgresContext *pg, .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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_units", - "SELECT cu.unit_serial" - " ,cu.unit" - " ,cu.unit_name_long" - " ,cu.unit_name_short" - " ,cu.unit_name_long_i18n" - " ,cu.unit_name_short_i18n" - " ,cu.unit_allow_fraction" - " ,cu.unit_precision_level" - " ,cu.unit_active" - " ,FALSE AS unit_builtin" - " FROM merchant_custom_units cu" - " UNION ALL " - "SELECT bu.unit_serial" - " ,bu.unit" - " ,bu.unit_name_long" - " ,bu.unit_name_short" - " ,bu.unit_name_long_i18n" - " ,bu.unit_name_short_i18n" - " ,COALESCE(bo.override_allow_fraction, bu.unit_allow_fraction)" - " ,COALESCE(bo.override_precision_level, bu.unit_precision_level)" - " ,COALESCE(bo.override_active, bu.unit_active)" - " ,TRUE AS unit_builtin" - " FROM merchant.merchant_builtin_units bu" - " LEFT JOIN merchant_builtin_unit_overrides bo" - " ON bo.builtin_unit_serial = bu.unit_serial" - " ORDER BY unit"); + PREPARE (pg, + "lookup_units", + "WITH mi AS (" + " SELECT merchant_serial FROM merchant_instances WHERE merchant_id=$1" + ")" + "SELECT cu.unit_serial" + " ,cu.unit" + " ,cu.unit_name_long" + " ,cu.unit_name_short" + " ,cu.unit_name_long_i18n" + " ,cu.unit_name_short_i18n" + " ,cu.unit_allow_fraction" + " ,cu.unit_precision_level" + " ,cu.unit_active" + " ,FALSE AS unit_builtin" + " FROM merchant_custom_units cu" + " JOIN mi ON cu.merchant_serial = mi.merchant_serial" + " UNION ALL " + "SELECT bu.unit_serial" + " ,bu.unit" + " ,bu.unit_name_long" + " ,bu.unit_name_short" + " ,bu.unit_name_long_i18n" + " ,bu.unit_name_short_i18n" + " ,COALESCE(bo.override_allow_fraction, bu.unit_allow_fraction)" + " ,COALESCE(bo.override_precision_level, bu.unit_precision_level)" + " ,COALESCE(bo.override_active, bu.unit_active)" + " ,TRUE AS unit_builtin" + " FROM merchant_builtin_units bu" + " JOIN mi ON TRUE" + " LEFT JOIN merchant_builtin_unit_overrides bo" + " ON bo.builtin_unit_serial = bu.unit_serial" + " AND bo.merchant_serial = mi.merchant_serial" + " ORDER BY unit"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_units", params, &lookup_units_cb, &luc); diff --git a/src/backenddb/lookup_webhook.c b/src/backenddb/lookup_webhook.c @@ -32,26 +32,25 @@ TALER_MERCHANTDB_lookup_webhook (struct TALER_MERCHANTDB_PostgresContext *pg, struct TALER_MERCHANTDB_WebhookDetails *wb) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (webhook_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_webhook", - "SELECT" - " event_type" - ",url" - ",http_method" - ",header_template" - ",body_template" - " FROM merchant_webhook" - " WHERE webhook_id=$1"); + PREPARE (pg, + "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"); if (NULL == wb) { @@ -60,7 +59,7 @@ TALER_MERCHANTDB_lookup_webhook (struct TALER_MERCHANTDB_PostgresContext *pg, }; return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_webhook", params, rs_null); } @@ -85,7 +84,7 @@ TALER_MERCHANTDB_lookup_webhook (struct TALER_MERCHANTDB_PostgresContext *pg, }; return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "lookup_webhook", params, rs); } diff --git a/src/backenddb/lookup_webhook_by_event.c b/src/backenddb/lookup_webhook_by_event.c @@ -124,31 +124,30 @@ TALER_MERCHANTDB_lookup_webhook_by_event (struct TALER_MERCHANTDB_PostgresContex }; 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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_webhook_by_event", - "SELECT" - " webhook_serial" - ",event_type" - ",url" - ",http_method" - ",header_template" - ",body_template" - " FROM merchant_webhook" - " WHERE event_type=$1"); + PREPARE (pg, + "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"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_webhook_by_event", params, &lookup_webhook_by_event_cb, &wlc); diff --git a/src/backenddb/lookup_webhooks.c b/src/backenddb/lookup_webhooks.c @@ -104,25 +104,24 @@ TALER_MERCHANTDB_lookup_webhooks (struct TALER_MERCHANTDB_PostgresContext *pg, .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; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "lookup_webhooks", - "SELECT" - " webhook_id" - ",event_type" - " FROM merchant_webhook"); + PREPARE (pg, + "lookup_webhooks", + "SELECT" + " webhook_id" + ",event_type" + " FROM merchant_webhook" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "lookup_webhooks", params, &lookup_webhooks_cb, &wlc); diff --git a/src/backenddb/mark_contract_paid.c b/src/backenddb/mark_contract_paid.c @@ -33,6 +33,7 @@ TALER_MERCHANTDB_mark_contract_paid (struct TALER_MERCHANTDB_PostgresContext *pg int16_t choice_index) { 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_string (session_id), (choice_index >= 0) @@ -41,19 +42,14 @@ TALER_MERCHANTDB_mark_contract_paid (struct TALER_MERCHANTDB_PostgresContext *pg GNUNET_PQ_query_param_end }; struct GNUNET_PQ_QueryParam uparams[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_auto_from_type (h_contract_terms), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_paid[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_sold[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_del[PG_PREP_INSTANCE_NAME_MAX]; /* Session ID must always be given by the caller. */ GNUNET_assert (NULL != session_id); - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); /* no preflight check here, run in transaction by caller! */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -61,55 +57,65 @@ TALER_MERCHANTDB_mark_contract_paid (struct TALER_MERCHANTDB_PostgresContext *pg GNUNET_h2s (&h_contract_terms->hash), instance_id, session_id); - PREPARE_INSTANCE (pg, - stmt_paid, - "mark_contract_paid", - "UPDATE merchant_contract_terms SET" - " paid=TRUE" - ",session_id=$2" - ",choice_index=$3" - " WHERE h_contract_terms=$1"); + PREPARE (pg, + "mark_contract_paid", + "UPDATE merchant_contract_terms SET" + " paid=TRUE" + ",session_id=$3" + ",choice_index=$4" + " WHERE h_contract_terms=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt_paid, + "mark_contract_paid", params); if (qs <= 0) return qs; - PREPARE_INSTANCE (pg, - stmt_sold, - "mark_inventory_sold", - "UPDATE merchant_inventory SET" - " total_sold = total_sold" - " + order_locks.total_locked" - " + ((merchant_inventory.total_sold_frac::BIGINT" - " + order_locks.total_locked_frac::BIGINT) / 1000000)" - " ,total_sold_frac =" - " ((merchant_inventory.total_sold_frac::BIGINT" - " + order_locks.total_locked_frac::BIGINT) % 1000000)::INT4" - " FROM (SELECT total_locked,total_locked_frac,product_serial" - " FROM merchant_order_locks" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$1)" - " ) AS order_locks" - " WHERE merchant_inventory.product_serial" - " =order_locks.product_serial"); + PREPARE (pg, + "mark_inventory_sold", + "UPDATE merchant_inventory SET" + " total_sold = total_sold" + " + order_locks.total_locked" + " + ((merchant_inventory.total_sold_frac::BIGINT" + " + order_locks.total_locked_frac::BIGINT) / 1000000)" + " ,total_sold_frac =" + " ((merchant_inventory.total_sold_frac::BIGINT" + " + order_locks.total_locked_frac::BIGINT) % 1000000)::INT4" + " FROM (SELECT total_locked,total_locked_frac,product_serial" + " FROM merchant_order_locks" + " 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))" + " ) AS order_locks" + " WHERE merchant_inventory.product_serial" + " =order_locks.product_serial"); qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt_sold, + "mark_inventory_sold", uparams); if (qs < 0) return qs; /* 0: no inventory management, that's OK! */ /* ON DELETE CASCADE deletes from merchant_order_locks */ - PREPARE_INSTANCE (pg, - stmt_del, - "delete_completed_order", - "DELETE" - " FROM merchant_orders" - " WHERE order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$1)"); + PREPARE (pg, + "delete_completed_order", + "WITH md AS" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1) " + "DELETE" + " FROM merchant_orders" + " WHERE order_serial=" + " (SELECT order_serial" + " FROM merchant_contract_terms" + " JOIN md USING (merchant_serial)" + " WHERE h_contract_terms=$2)"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt_del, + "delete_completed_order", uparams); } diff --git a/src/backenddb/mark_order_wired.c b/src/backenddb/mark_order_wired.c @@ -34,18 +34,14 @@ TALER_MERCHANTDB_mark_order_wired (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "mark_order_wired", - "UPDATE merchant_contract_terms SET" - " wired=TRUE" - " WHERE order_serial=$1"); + PREPARE (pg, + "mark_order_wired", + "UPDATE merchant_contract_terms SET" + " wired=TRUE" + " WHERE order_serial=$1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "mark_order_wired", params); } diff --git a/src/backenddb/meson.build b/src/backenddb/meson.build @@ -14,7 +14,6 @@ libtalermerchantdb = library( 'event_listen.c', 'event_notify.c', 'preflight.c', - 'set_instance.c', 'account_kyc_get_outdated.c', 'account_kyc_get_status.c', 'account_kyc_set_failed.c', diff --git a/src/backenddb/pg.c b/src/backenddb/pg.c @@ -85,7 +85,6 @@ void TALER_MERCHANTDB_disconnect (struct TALER_MERCHANTDB_PostgresContext *pg) { GNUNET_PQ_disconnect (pg->conn); - GNUNET_free (pg->current_merchant_id); GNUNET_free (pg); } diff --git a/src/backenddb/pg_account_kyc_get_outdated.sql b/src/backenddb/pg_account_kyc_get_outdated.sql @@ -1,57 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.account_kyc_get_outdated(INT8); -CREATE FUNCTION merchant.account_kyc_get_outdated(IN p_now INT8) -RETURNS TABLE( - out_merchant_id TEXT, - out_h_wire BYTEA, - out_exchange_url TEXT) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - inner_rec RECORD; -BEGIN - FOR rec IN - SELECT merchant_serial, merchant_id FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - FOR inner_rec IN - EXECUTE format('SELECT ma.h_wire AS h_wire, kyc.exchange_url AS exchange_url' - ' FROM %I.merchant_kyc kyc' - ' JOIN %I.merchant_accounts ma USING (account_serial)' - ' WHERE kyc.next_kyc_poll < $1' - ' ORDER BY kyc.next_kyc_poll ASC', s, s) - USING p_now - LOOP - out_merchant_id := rec.merchant_id; - out_h_wire := inner_rec.h_wire; - out_exchange_url := inner_rec.exchange_url; - RETURN NEXT; - END LOOP; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.account_kyc_get_outdated(INT8) - IS 'Returns one row per outdated KYC entry across all instance schemas.' - ' An entry is outdated if its next_kyc_poll value is less than p_now.'; diff --git a/src/backenddb/pg_create_instance_schema.sql b/src/backenddb/pg_create_instance_schema.sql @@ -1,39 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.create_instance_schema(BIGINT); -CREATE FUNCTION merchant.create_instance_schema(in_merchant_serial BIGINT) - RETURNS void - LANGUAGE plpgsql - AS $FN$ -DECLARE - s TEXT := 'merchant_instance_' || in_merchant_serial::TEXT; -BEGIN - -- Resolve bare type references (statistic_range, taler_amount_currency, ...) - -- and bare table references in trigger / procedure bodies against the new - -- per-instance schema first, then fall through to merchant. - EXECUTE format('SET LOCAL search_path TO %I, merchant', s); -#include "pg_create_instance_schema_tables.sql.fragment" -#include "pg_create_instance_schema_triggers.sql.fragment" -#include "pg_create_instance_schema_procedures.sql.fragment" -END; -$FN$; -COMMENT ON FUNCTION merchant.create_instance_schema(BIGINT) - IS 'Constructs the per-instance schema merchant_instance_<merchant_serial>' - ' with all per-instance tables, indexes, foreign keys, trigger functions,' - ' triggers and stored procedures. Called from' - ' merchant_instances_after_insert_trigger() whenever a new instance row' - ' is inserted into merchant.merchant_instances.'; diff --git a/src/backenddb/pg_create_instance_schema_procedures.sql.fragment b/src/backenddb/pg_create_instance_schema_procedures.sql.fragment @@ -1,2214 +0,0 @@ --- ===================================================================== --- Per-instance schema constructor: stored procedures and functions. --- --- Embedded inside merchant.create_instance_schema(BIGINT); local var --- `s TEXT` holds the per-instance schema name. All DDL is via --- EXECUTE format(...) with %I quoting. --- --- The merchant_serial column has been dropped from every per-instance --- table; the in_instance_id / in_instance_name / in_merchant_serial / --- out_no_instance parameters and the merchant_serial-resolution --- prologue are removed from each procedure. Helper functions --- (replace_placeholder, interval_to_start, ...) and global tables --- (merchant_instances, merchant_exchange_*, merchant_builtin_units, --- merchant_donau_keys) keep their `merchant.` qualifier. --- ===================================================================== - - -- ------------------------------------------------------------------- - -- Statistic bump procedures (6 procedures) - -- ------------------------------------------------------------------- - - -- merchant_do_bump_amount_bucket_stat - EXECUTE format($OUTER$ - CREATE PROCEDURE %I.merchant_do_bump_amount_bucket_stat(IN in_slug text, IN in_timestamp timestamp without time zone, IN in_delta merchant.taler_amount_currency) - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_meta INT8; - my_range statistic_range; - my_bucket_start INT8; - my_curs CURSOR (arg_slug TEXT) - FOR SELECT UNNEST(ranges) - FROM merchant_statistic_bucket_meta - WHERE slug=arg_slug; - BEGIN - SELECT bmeta_serial_id - INTO my_meta - FROM merchant_statistic_bucket_meta - WHERE slug=in_slug - AND stype='amount'; - IF NOT FOUND - THEN - RETURN; - END IF; - OPEN my_curs (arg_slug:=in_slug); - LOOP - FETCH NEXT - FROM my_curs - INTO my_range; - EXIT WHEN NOT FOUND; - SELECT * - INTO my_bucket_start - FROM merchant.interval_to_start (in_timestamp, my_range); - UPDATE merchant_statistic_bucket_amount - SET - cumulative_value = cumulative_value + (in_delta).val - + CASE - WHEN (in_delta).frac + cumulative_frac >= 100000000 - THEN 1 - ELSE 0 - END, - cumulative_frac = cumulative_frac + (in_delta).frac - - CASE - WHEN (in_delta).frac + cumulative_frac >= 100000000 - THEN 100000000 - ELSE 0 - END - WHERE bmeta_serial_id=my_meta - AND curr=(in_delta).curr - AND bucket_start=my_bucket_start - AND bucket_range=my_range; - IF NOT FOUND - THEN - INSERT INTO merchant_statistic_bucket_amount - (bmeta_serial_id - ,bucket_start - ,bucket_range - ,curr - ,cumulative_value - ,cumulative_frac - ) VALUES ( - my_meta - ,my_bucket_start - ,my_range - ,(in_delta).curr - ,(in_delta).val - ,(in_delta).frac); - END IF; - END LOOP; - CLOSE my_curs; - END $BODY$ - $OUTER$, s); - - -- merchant_do_bump_amount_interval_stat - EXECUTE format($OUTER$ - CREATE PROCEDURE %I.merchant_do_bump_amount_interval_stat(IN in_slug text, IN in_timestamp timestamp without time zone, IN in_delta merchant.taler_amount_currency) - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_now INT8; - my_record RECORD; - my_meta INT8; - my_ranges INT8[]; - my_precisions INT8[]; - my_x INT; - my_rangex INT8; - my_precisionx INT8; - my_start INT8; - my_event INT8; - BEGIN - my_now = ROUND(EXTRACT(epoch FROM CURRENT_TIMESTAMP(0)::TIMESTAMP) * 1000000)::INT8 / 1000 / 1000; - SELECT imeta_serial_id - ,ranges - ,precisions - INTO my_record - FROM merchant_statistic_interval_meta - WHERE slug=in_slug - AND stype='amount'; - IF NOT FOUND - THEN - RETURN; - END IF; - my_start = ROUND(EXTRACT(epoch FROM in_timestamp) * 1000000)::INT8 / 1000 / 1000; - my_precisions = my_record.precisions; - my_ranges = my_record.ranges; - my_rangex = NULL; - FOR my_x IN 1..COALESCE(array_length(my_ranges,1),0) - LOOP - IF my_now - my_ranges[my_x] < my_start - THEN - my_rangex = my_ranges[my_x]; - my_precisionx = my_precisions[my_x]; - EXIT; - END IF; - END LOOP; - IF my_rangex IS NULL - THEN - RETURN; - END IF; - my_start = my_start - my_start %% my_precisionx; - my_meta = my_record.imeta_serial_id; - INSERT INTO merchant_statistic_amount_event AS msae - (imeta_serial_id - ,slot - ,delta_curr - ,delta_value - ,delta_frac - ) VALUES ( - my_meta - ,my_start - ,(in_delta).curr - ,(in_delta).val - ,(in_delta).frac - ) - ON CONFLICT (imeta_serial_id, slot, delta_curr) - DO UPDATE SET - delta_value = msae.delta_value + (in_delta).val - + CASE - WHEN (in_delta).frac + msae.delta_frac >= 100000000 - THEN 1 - ELSE 0 - END, - delta_frac = msae.delta_frac + (in_delta).frac - - CASE - WHEN (in_delta).frac + msae.delta_frac >= 100000000 - THEN 100000000 - ELSE 0 - END - RETURNING aevent_serial_id - INTO my_event; - UPDATE merchant_statistic_interval_amount - SET - cumulative_value = cumulative_value + (in_delta).val - + CASE - WHEN (in_delta).frac + cumulative_frac >= 100000000 - THEN 1 - ELSE 0 - END, - cumulative_frac = cumulative_frac + (in_delta).frac - - CASE - WHEN (in_delta).frac + cumulative_frac >= 100000000 - THEN 100000000 - ELSE 0 - END - WHERE imeta_serial_id=my_meta - AND range=my_rangex - AND curr=(in_delta).curr; - IF NOT FOUND - THEN - INSERT INTO merchant_statistic_interval_amount - (imeta_serial_id - ,range - ,event_delimiter - ,curr - ,cumulative_value - ,cumulative_frac - ) VALUES ( - my_meta - ,my_rangex - ,my_event - ,(in_delta).curr - ,(in_delta).val - ,(in_delta).frac); - END IF; - END $BODY$ - $OUTER$, s); - - -- merchant_do_bump_amount_stat - EXECUTE format($OUTER$ - CREATE PROCEDURE %I.merchant_do_bump_amount_stat(IN in_slug text, IN in_timestamp timestamp without time zone, IN in_delta merchant.taler_amount_currency) - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_amount_bucket_stat (in_slug, in_timestamp, in_delta); - CALL merchant_do_bump_amount_interval_stat (in_slug, in_timestamp, in_delta); - END $BODY$ - $OUTER$, s); - - -- merchant_do_bump_number_bucket_stat - EXECUTE format($OUTER$ - CREATE PROCEDURE %I.merchant_do_bump_number_bucket_stat(IN in_slug text, IN in_timestamp timestamp without time zone, IN in_delta bigint) - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_meta INT8; - my_range statistic_range; - my_bucket_start INT8; - my_curs CURSOR (arg_slug TEXT) - FOR SELECT UNNEST(ranges) - FROM merchant_statistic_bucket_meta - WHERE slug=arg_slug; - BEGIN - SELECT bmeta_serial_id - INTO my_meta - FROM merchant_statistic_bucket_meta - WHERE slug=in_slug - AND stype='number'; - IF NOT FOUND - THEN - RETURN; - END IF; - OPEN my_curs (arg_slug:=in_slug); - LOOP - FETCH NEXT - FROM my_curs - INTO my_range; - EXIT WHEN NOT FOUND; - SELECT * - INTO my_bucket_start - FROM merchant.interval_to_start (in_timestamp, my_range); - UPDATE merchant_statistic_bucket_counter - SET cumulative_number = cumulative_number + in_delta - WHERE bmeta_serial_id=my_meta - AND bucket_start=my_bucket_start - AND bucket_range=my_range; - IF NOT FOUND - THEN - INSERT INTO merchant_statistic_bucket_counter - (bmeta_serial_id - ,bucket_start - ,bucket_range - ,cumulative_number - ) VALUES ( - my_meta - ,my_bucket_start - ,my_range - ,in_delta); - END IF; - END LOOP; - CLOSE my_curs; - END $BODY$ - $OUTER$, s); - - -- merchant_do_bump_number_interval_stat - EXECUTE format($OUTER$ - CREATE PROCEDURE %I.merchant_do_bump_number_interval_stat(IN in_slug text, IN in_timestamp timestamp without time zone, IN in_delta bigint) - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_now INT8; - my_record RECORD; - my_meta INT8; - my_ranges INT8[]; - my_precisions INT8[]; - my_rangex INT8; - my_precisionx INT8; - my_start INT8; - my_event INT8; - BEGIN - my_now = ROUND(EXTRACT(epoch FROM CURRENT_TIMESTAMP(0)::TIMESTAMP) * 1000000)::INT8 / 1000 / 1000; - SELECT imeta_serial_id - ,ranges AS ranges - ,precisions AS precisions - INTO my_record - FROM merchant_statistic_interval_meta - WHERE slug=in_slug - AND stype='number'; - IF NOT FOUND - THEN - RETURN; - END IF; - my_start = ROUND(EXTRACT(epoch FROM in_timestamp) * 1000000)::INT8 / 1000 / 1000; - my_precisions = my_record.precisions; - my_ranges = my_record.ranges; - my_rangex = NULL; - FOR my_x IN 1..COALESCE(array_length(my_ranges,1),0) - LOOP - IF my_now - my_ranges[my_x] < my_start - THEN - my_rangex = my_ranges[my_x]; - my_precisionx = my_precisions[my_x]; - EXIT; - END IF; - END LOOP; - IF my_rangex IS NULL - THEN - RETURN; - END IF; - my_meta = my_record.imeta_serial_id; - my_start = my_start - my_start %% my_precisionx; - INSERT INTO merchant_statistic_counter_event AS msce - (imeta_serial_id - ,slot - ,delta) - VALUES - (my_meta - ,my_start - ,in_delta) - ON CONFLICT (imeta_serial_id, slot) - DO UPDATE SET - delta = msce.delta + in_delta - RETURNING nevent_serial_id - INTO my_event; - UPDATE merchant_statistic_interval_counter - SET cumulative_number = cumulative_number + in_delta - WHERE imeta_serial_id = my_meta - AND range=my_rangex; - IF NOT FOUND - THEN - INSERT INTO merchant_statistic_interval_counter - (imeta_serial_id - ,range - ,event_delimiter - ,cumulative_number - ) VALUES ( - my_meta - ,my_rangex - ,my_event - ,in_delta); - END IF; - END $BODY$ - $OUTER$, s); - - -- merchant_do_bump_number_stat - EXECUTE format($OUTER$ - CREATE PROCEDURE %I.merchant_do_bump_number_stat(IN in_slug text, IN in_timestamp timestamp without time zone, IN in_delta bigint) - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_number_bucket_stat (in_slug, in_timestamp, in_delta); - CALL merchant_do_bump_number_interval_stat (in_slug, in_timestamp, in_delta); - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- Account / KYC functions - -- ------------------------------------------------------------------- - - -- merchant_do_account_kyc_get_status - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_account_kyc_get_status(in_now bigint, in_exchange_url text, in_h_wire bytea) RETURNS TABLE(out_h_wire bytea, out_payto_uri text, out_exchange_url text, out_kyc_timestamp bigint, out_kyc_ok boolean, out_access_token bytea, out_exchange_http_status integer, out_exchange_ec_code integer, out_aml_review boolean, out_jaccount_limits text) - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_account_serial INT8; - my_h_wire BYTEA; - my_payto_uri TEXT; - my_kyc_record RECORD; - BEGIN - FOR my_account_serial, my_h_wire, my_payto_uri - IN SELECT account_serial, h_wire, payto_uri - FROM merchant_accounts - WHERE active - AND (in_h_wire IS NULL OR h_wire = in_h_wire) - ORDER BY account_serial ASC - LOOP - FOR my_kyc_record IN - SELECT - mk.kyc_serial_id - ,mk.exchange_url - ,mk.kyc_timestamp - ,mk.kyc_ok - ,mk.access_token - ,mk.exchange_http_status - ,mk.exchange_ec_code - ,mk.aml_review - ,mk.jaccount_limits::TEXT - FROM merchant_kyc mk - WHERE mk.account_serial = my_account_serial - AND (in_exchange_url IS NULL OR mk.exchange_url = in_exchange_url) - ORDER BY mk.kyc_serial_id ASC - LOOP - UPDATE merchant_kyc - SET next_kyc_poll=in_now - WHERE kyc_serial_id = my_kyc_record.kyc_serial_id; - NOTIFY XDQM4Z4N0D3GX0H9JEXH70EBC2T3KY7HC0TJB0Z60D2H781RXR6AG; - RETURN QUERY - SELECT - my_h_wire, - my_payto_uri, - my_kyc_record.exchange_url, - my_kyc_record.kyc_timestamp, - my_kyc_record.kyc_ok, - my_kyc_record.access_token, - my_kyc_record.exchange_http_status, - my_kyc_record.exchange_ec_code, - my_kyc_record.aml_review, - my_kyc_record.jaccount_limits::TEXT; - END LOOP; - IF NOT FOUND - THEN - RETURN QUERY - SELECT - my_h_wire, - my_payto_uri, - NULL::TEXT, - NULL::INT8, - NULL::BOOLEAN, - NULL::BYTEA, - NULL::INT4, - NULL::INT4, - NULL::BOOLEAN, - NULL::TEXT; - END IF; - END LOOP; - END $BODY$ - $OUTER$, s); - - -- merchant_do_account_kyc_set_failed - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_account_kyc_set_failed(in_h_wire bytea, in_exchange_url text, in_timestamp bigint, in_exchange_http_status integer, in_kyc_ok boolean, in_notify_str text, in_notify2_str text, OUT out_no_account boolean) RETURNS boolean - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_account_serial INT8; - BEGIN - out_no_account=FALSE; - SELECT account_serial - INTO my_account_serial - FROM merchant_accounts - WHERE h_wire=in_h_wire; - IF NOT FOUND - THEN - out_no_account=TRUE; - RETURN; - END IF; - UPDATE merchant_kyc - SET kyc_timestamp=in_timestamp - ,kyc_ok=in_kyc_ok - ,exchange_http_status=in_exchange_http_status - ,exchange_ec_code=0 - WHERE account_serial=my_account_serial - AND exchange_url=in_exchange_url; - IF NOT FOUND - THEN - INSERT INTO merchant_kyc - (kyc_timestamp - ,kyc_ok - ,account_serial - ,exchange_url - ,exchange_http_status) - VALUES - (in_timestamp - ,in_kyc_ok - ,my_account_serial - ,in_exchange_url - ,in_exchange_http_status); - END IF; - EXECUTE FORMAT ( - 'NOTIFY %%s' - ,in_notify_str); - EXECUTE FORMAT ( - 'NOTIFY %%s' - ,in_notify2_str); - END $BODY$ - $OUTER$, s); - - -- merchant_do_account_kyc_set_status - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_account_kyc_set_status(in_h_wire bytea, in_exchange_url text, in_timestamp bigint, in_exchange_http_status integer, in_exchange_ec_code integer, in_access_token bytea, in_jlimits jsonb, in_aml_active boolean, in_kyc_ok boolean, in_notify_str text, in_notify2_str text, in_rule_gen bigint, in_next_time bigint, in_kyc_backoff bigint, OUT out_no_account boolean) RETURNS boolean - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_account_serial INT8; - BEGIN - out_no_account=FALSE; - SELECT account_serial - INTO my_account_serial - FROM merchant_accounts - WHERE h_wire=in_h_wire; - IF NOT FOUND - THEN - out_no_account=TRUE; - RETURN; - END IF; - UPDATE merchant_kyc - SET kyc_timestamp=in_timestamp - ,kyc_ok=in_kyc_ok - ,jaccount_limits=in_jlimits - ,aml_review=in_aml_active - ,exchange_http_status=in_exchange_http_status - ,exchange_ec_code=in_exchange_ec_code - ,access_token=in_access_token - ,last_rule_gen=in_rule_gen - ,next_kyc_poll=in_next_time - ,kyc_backoff=in_kyc_backoff - WHERE account_serial=my_account_serial - AND exchange_url=in_exchange_url; - IF NOT FOUND - THEN - INSERT INTO merchant_kyc - (kyc_timestamp - ,kyc_ok - ,account_serial - ,exchange_url - ,jaccount_limits - ,aml_review - ,exchange_http_status - ,exchange_ec_code - ,access_token - ,last_rule_gen - ,next_kyc_poll - ,kyc_backoff) - VALUES - (in_timestamp - ,in_kyc_ok - ,my_account_serial - ,in_exchange_url - ,in_jlimits - ,in_aml_active - ,in_exchange_http_status - ,in_exchange_ec_code - ,in_access_token - ,in_rule_gen - ,in_next_time - ,in_kyc_backoff); - END IF; - EXECUTE FORMAT ( - 'NOTIFY %%s' - ,in_notify_str); - EXECUTE FORMAT ( - 'NOTIFY %%s' - ,in_notify2_str); - END $BODY$ - $OUTER$, s); - - -- merchant_do_activate_account - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_activate_account(in_h_wire bytea, in_salt bytea, in_full_payto text, in_credit_facade_url text, in_credit_facade_credentials text, in_extra_wire_subject_metadata text, OUT out_h_wire bytea, OUT out_salt bytea, OUT out_not_found boolean, OUT out_conflict boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE my_active BOOL; - DECLARE my_cfu TEXT; - DECLARE my_cfc TEXT; - DECLARE my_ewsm TEXT; - DECLARE my_h_wire BYTEA; - DECLARE my_salt BYTEA; - BEGIN - out_not_found = FALSE; - out_conflict = FALSE; - out_h_wire = in_h_wire; - out_salt = in_salt; - INSERT INTO merchant_accounts - AS ma - (h_wire - ,salt - ,payto_uri - ,credit_facade_url - ,credit_facade_credentials - ,active - ,extra_wire_subject_metadata - ) VALUES ( - in_h_wire - ,in_salt - ,in_full_payto - ,in_credit_facade_url - ,in_credit_facade_credentials::JSONB - ,TRUE - ,in_extra_wire_subject_metadata - ) ON CONFLICT DO NOTHING; - IF FOUND - THEN - NOTIFY XDQM4Z4N0D3GX0H9JEXH70EBC2T3KY7HC0TJB0Z60D2H781RXR6AG; - RETURN; - END IF; - SELECT h_wire - ,salt - ,active - ,credit_facade_url - ,credit_facade_credentials::TEXT - ,extra_wire_subject_metadata - INTO my_h_wire - ,my_salt - ,my_active - ,my_cfu - ,my_cfc - ,my_ewsm - FROM merchant_accounts - WHERE payto_uri=in_full_payto; - IF NOT FOUND - THEN - out_not_found = TRUE; - RETURN; - END IF; - IF (my_active AND - (ROW (my_cfu - ,my_cfc - ,my_ewsm) - IS DISTINCT FROM - ROW (in_credit_facade_url - ,in_credit_facade_credentials - ,in_extra_wire_subject_metadata))) - THEN - out_conflict = TRUE; - RETURN; - END IF; - out_salt = my_salt; - out_h_wire = my_h_wire; - IF my_active - THEN - RETURN; - END IF; - UPDATE merchant_accounts - SET active=TRUE - ,credit_facade_url=in_credit_facade_url - ,credit_facade_credentials=in_credit_facade_credentials::JSONB - ,extra_wire_subject_metadata=in_extra_wire_subject_metadata - WHERE h_wire=out_h_wire; - NOTIFY XDQM4Z4N0D3GX0H9JEXH70EBC2T3KY7HC0TJB0Z60D2H781RXR6AG; - END $BODY$ - $OUTER$, s); - - -- merchant_do_inactivate_account - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_inactivate_account(in_h_wire bytea, OUT out_found boolean) RETURNS boolean - LANGUAGE plpgsql - AS $BODY$ - BEGIN - UPDATE merchant_accounts - SET active=FALSE - WHERE h_wire=in_h_wire; - out_found = FOUND; - IF out_found - THEN - NOTIFY XDQM4Z4N0D3GX0H9JEXH70EBC2T3KY7HC0TJB0Z60D2H781RXR6AG; - END IF; - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- Unit functions - -- ------------------------------------------------------------------- - - -- merchant_do_delete_unit - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_delete_unit(in_unit_id text, OUT out_no_unit boolean, OUT out_builtin_conflict boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_unit merchant_custom_units%%ROWTYPE; - BEGIN - out_no_unit := FALSE; - out_builtin_conflict := FALSE; - - SELECT * - INTO my_unit - FROM merchant_custom_units - WHERE unit = in_unit_id - FOR UPDATE; - - IF NOT FOUND THEN - IF EXISTS (SELECT 1 FROM merchant.merchant_builtin_units bu WHERE bu.unit = in_unit_id) THEN - out_builtin_conflict := TRUE; - ELSE - out_no_unit := TRUE; - END IF; - RETURN; - END IF; - - DELETE FROM merchant_custom_units - WHERE unit_serial = my_unit.unit_serial; - - RETURN; - END $BODY$ - $OUTER$, s); - - -- merchant_do_insert_unit - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_insert_unit(in_unit text, in_unit_name_long text, in_unit_name_short text, in_unit_name_long_i18n bytea, in_unit_name_short_i18n bytea, in_unit_allow_fraction boolean, in_unit_precision_level integer, in_unit_active boolean, OUT out_conflict boolean, OUT out_unit_serial bigint) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - BEGIN - -- Reject attempts to shadow builtin identifiers. - IF EXISTS ( - SELECT 1 FROM merchant.merchant_builtin_units bu WHERE bu.unit = in_unit - ) THEN - out_conflict := TRUE; - out_unit_serial := NULL; - RETURN; - END IF; - - INSERT INTO merchant_custom_units ( - unit, - unit_name_long, - unit_name_short, - unit_name_long_i18n, - unit_name_short_i18n, - unit_allow_fraction, - unit_precision_level, - unit_active) - VALUES ( - in_unit, - in_unit_name_long, - in_unit_name_short, - in_unit_name_long_i18n, - in_unit_name_short_i18n, - in_unit_allow_fraction, - in_unit_precision_level, - in_unit_active) - ON CONFLICT (unit) DO NOTHING - RETURNING unit_serial - INTO out_unit_serial; - - IF FOUND THEN - out_conflict := FALSE; - RETURN; - END IF; - - -- Conflict: custom unit already exists. - SELECT unit_serial - INTO out_unit_serial - FROM merchant_custom_units - WHERE unit = in_unit; - - out_conflict := TRUE; - END $BODY$ - $OUTER$, s); - - -- merchant_do_update_unit - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_update_unit(in_unit_id text, in_unit_name_long text, in_unit_name_long_i18n bytea, in_unit_name_short text, in_unit_name_short_i18n bytea, in_unit_allow_fraction boolean, in_unit_precision_level integer, in_unit_active boolean, OUT out_no_unit boolean, OUT out_builtin_conflict boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_custom merchant_custom_units%%ROWTYPE; - my_builtin merchant.merchant_builtin_units%%ROWTYPE; - my_override merchant_builtin_unit_overrides%%ROWTYPE; - new_unit_name_long TEXT; - new_unit_name_short TEXT; - new_unit_name_long_i18n BYTEA; - new_unit_name_short_i18n BYTEA; - new_unit_allow_fraction BOOL; - new_unit_precision_level INT4; - new_unit_active BOOL; - old_unit_allow_fraction BOOL; - old_unit_precision_level INT4; - old_unit_active BOOL; - BEGIN - out_no_unit := FALSE; - out_builtin_conflict := FALSE; - - SELECT * - INTO my_custom - FROM merchant_custom_units - WHERE unit = in_unit_id - FOR UPDATE; - - IF FOUND THEN - old_unit_allow_fraction := my_custom.unit_allow_fraction; - old_unit_precision_level := my_custom.unit_precision_level; - old_unit_active := my_custom.unit_active; - - new_unit_name_long := COALESCE (in_unit_name_long, my_custom.unit_name_long); - new_unit_name_short := COALESCE (in_unit_name_short, my_custom.unit_name_short); - new_unit_name_long_i18n := COALESCE (in_unit_name_long_i18n, - my_custom.unit_name_long_i18n); - new_unit_name_short_i18n := COALESCE (in_unit_name_short_i18n, - my_custom.unit_name_short_i18n); - new_unit_allow_fraction := COALESCE (in_unit_allow_fraction, - my_custom.unit_allow_fraction); - new_unit_precision_level := COALESCE (in_unit_precision_level, - my_custom.unit_precision_level); - IF NOT new_unit_allow_fraction THEN - new_unit_precision_level := 0; - END IF; - - new_unit_active := COALESCE (in_unit_active, my_custom.unit_active); - - UPDATE merchant_custom_units SET - unit_name_long = new_unit_name_long - ,unit_name_long_i18n = new_unit_name_long_i18n - ,unit_name_short = new_unit_name_short - ,unit_name_short_i18n = new_unit_name_short_i18n - ,unit_allow_fraction = new_unit_allow_fraction - ,unit_precision_level = new_unit_precision_level - ,unit_active = new_unit_active - WHERE unit_serial = my_custom.unit_serial; - - ASSERT FOUND,'SELECTED it earlier, should UPDATE it now'; - - IF old_unit_allow_fraction IS DISTINCT FROM new_unit_allow_fraction - OR old_unit_precision_level IS DISTINCT FROM new_unit_precision_level - THEN - UPDATE merchant_inventory SET - allow_fractional_quantity = new_unit_allow_fraction - , fractional_precision_level = new_unit_precision_level - WHERE unit = in_unit_id - AND allow_fractional_quantity = old_unit_allow_fraction - AND fractional_precision_level = old_unit_precision_level; - END IF; - RETURN; - END IF; - - -- Try builtin with overrides. - SELECT * - INTO my_builtin - FROM merchant.merchant_builtin_units - WHERE unit = in_unit_id; - - IF NOT FOUND THEN - out_no_unit := TRUE; - RETURN; - END IF; - - SELECT * - INTO my_override - FROM merchant_builtin_unit_overrides - WHERE builtin_unit_serial = my_builtin.unit_serial - FOR UPDATE; - - old_unit_allow_fraction := COALESCE (my_override.override_allow_fraction, - my_builtin.unit_allow_fraction); - old_unit_precision_level := COALESCE (my_override.override_precision_level, - my_builtin.unit_precision_level); - old_unit_active := COALESCE (my_override.override_active, - my_builtin.unit_active); - - -- Only policy flags can change for builtin units. - IF in_unit_name_long IS NOT NULL - OR in_unit_name_short IS NOT NULL - OR in_unit_name_long_i18n IS NOT NULL - OR in_unit_name_short_i18n IS NOT NULL THEN - out_builtin_conflict := TRUE; - RETURN; - END IF; - - new_unit_allow_fraction := COALESCE (in_unit_allow_fraction, - old_unit_allow_fraction); - new_unit_precision_level := COALESCE (in_unit_precision_level, - old_unit_precision_level); - IF NOT new_unit_allow_fraction THEN - new_unit_precision_level := 0; - END IF; - new_unit_active := COALESCE (in_unit_active, old_unit_active); - - INSERT INTO merchant_builtin_unit_overrides ( - builtin_unit_serial, - override_allow_fraction, - override_precision_level, - override_active) - VALUES (my_builtin.unit_serial, - new_unit_allow_fraction, - new_unit_precision_level, - new_unit_active) - ON CONFLICT (builtin_unit_serial) - DO UPDATE SET override_allow_fraction = EXCLUDED.override_allow_fraction - , override_precision_level = EXCLUDED.override_precision_level - , override_active = EXCLUDED.override_active; - - IF old_unit_allow_fraction IS DISTINCT FROM new_unit_allow_fraction - OR old_unit_precision_level IS DISTINCT FROM new_unit_precision_level - THEN - UPDATE merchant_inventory SET - allow_fractional_quantity = new_unit_allow_fraction - , fractional_precision_level = new_unit_precision_level - WHERE unit = in_unit_id - AND allow_fractional_quantity = old_unit_allow_fraction - AND fractional_precision_level = old_unit_precision_level; - END IF; - - RETURN; - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- Money pots - -- ------------------------------------------------------------------- - - -- merchant_do_increment_money_pots - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_increment_money_pots(ina_money_pots_ids bigint[], ina_increments merchant.taler_amount_currency[], OUT out_not_found boolean) RETURNS boolean - LANGUAGE plpgsql - AS $BODY$ - DECLARE - i INT; - ini_current_pot_id INT8; - ini_current_increment taler_amount_currency; - my_totals taler_amount_currency[]; - currency_found BOOL; - j INT; - BEGIN - out_not_found = FALSE; - IF ( COALESCE(array_length(ina_money_pots_ids, 1), 0) != - COALESCE(array_length(ina_increments, 1), 0) ) - THEN - RAISE EXCEPTION 'Array lengths must match'; - END IF; - FOR i IN 1..COALESCE(array_length(ina_money_pots_ids, 1),0) - LOOP - ini_current_pot_id = ina_money_pots_ids[i]; - ini_current_increment = ina_increments[i]; - SELECT pot_totals - INTO my_totals - FROM merchant_money_pots - WHERE money_pot_serial = ini_current_pot_id; - IF NOT FOUND - THEN - out_not_found = TRUE; - ELSE - currency_found = FALSE; - FOR j IN 1..COALESCE(array_length(my_totals, 1), 0) - LOOP - IF (my_totals[j]).currency = (ini_current_increment).currency - THEN - my_totals[j].frac - = my_totals[j].frac + ini_current_increment.frac; - my_totals[j].val - = my_totals[j].val + ini_current_increment.val; - IF my_totals[j].frac >= 100000000 - THEN - my_totals[j].frac = my_totals[j].frac - 100000000; - my_totals[j].val = my_totals[j].val + 1; - END IF; - currency_found = TRUE; - EXIT; - END IF; - END LOOP; - IF NOT currency_found - THEN - my_totals = array_append(my_totals, ini_current_increment); - END IF; - UPDATE merchant_money_pots - SET pot_totals = my_totals - WHERE money_pot_serial = ini_current_pot_id; - END IF; - END LOOP; - END $BODY$ - $OUTER$, s); - - -- merchant_do_update_money_pot - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_update_money_pot(in_money_pot_serial bigint, in_name text, in_description text, in_old_totals merchant.taler_amount_currency[], in_new_totals merchant.taler_amount_currency[], OUT out_conflict_total boolean, OUT out_conflict_name boolean, OUT out_not_found boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - BEGIN - BEGIN - UPDATE merchant_money_pots SET - money_pot_name=in_name - ,money_pot_description=in_description - ,pot_totals=COALESCE(in_new_totals, pot_totals) - WHERE money_pot_serial=in_money_pot_serial - AND ( (in_old_totals IS NULL) OR (pot_totals=in_old_totals) ); - IF NOT FOUND - THEN - PERFORM FROM merchant_money_pots - WHERE money_pot_serial=in_money_pot_serial; - out_conflict_total = FOUND; - out_not_found = NOT FOUND; - out_conflict_name = FALSE; - ELSE - out_conflict_total = FALSE; - out_not_found = FALSE; - out_conflict_name = FALSE; - END IF; - RETURN; - EXCEPTION - WHEN unique_violation - THEN - out_conflict_name = TRUE; - out_conflict_total = FALSE; - out_not_found = FALSE; - RETURN; - END; - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- Deposit confirmations and transfers - -- ------------------------------------------------------------------- - - -- merchant_do_insert_deposit_confirmation - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_insert_deposit_confirmation(in_h_contract_terms bytea, in_deposit_timestamp bigint, in_exchange_url text, in_total_without_fee merchant.taler_amount_currency, in_wire_fee merchant.taler_amount_currency, in_h_wire bytea, in_exchange_sig bytea, in_exchange_pub bytea, in_wire_transfer_deadline bigint, in_notify_arg_str text, OUT out_no_order boolean, OUT out_no_account boolean, OUT out_no_signkey boolean, OUT out_conflict boolean, OUT out_deposit_confirmation_serial bigint) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_order_serial INT8; - my_account_serial INT8; - my_signkey_serial INT8; - my_record RECORD; - my_bank_serial_id INT8; - my_credit_amount taler_amount_currency; - BEGIN - out_no_order=TRUE; - out_no_account=TRUE; - out_no_signkey=TRUE; - out_conflict=FALSE; - out_deposit_confirmation_serial=0; - SELECT account_serial - INTO my_account_serial - FROM merchant_accounts - WHERE h_wire=in_h_wire; - IF NOT FOUND - THEN - RETURN; - END IF; - out_no_account=FALSE; - SELECT signkey_serial - INTO my_signkey_serial - FROM merchant.merchant_exchange_signing_keys - WHERE exchange_pub=in_exchange_pub - ORDER BY start_date DESC - LIMIT 1; - IF NOT FOUND - THEN - RETURN; - END IF; - out_no_signkey=FALSE; - SELECT order_serial - INTO my_order_serial - FROM merchant_contract_terms - WHERE h_contract_terms=in_h_contract_terms; - IF NOT FOUND - THEN - RETURN; - END IF; - out_no_order=FALSE; - SELECT deposit_confirmation_serial - ,deposit_timestamp - ,exchange_url - ,total_without_fee - ,wire_fee - ,wire_transfer_deadline - ,account_serial - INTO my_record - FROM merchant_deposit_confirmations - WHERE order_serial=my_order_serial - AND exchange_url=in_exchange_url; - IF NOT FOUND - THEN - INSERT INTO merchant_deposit_confirmations - (order_serial - ,deposit_timestamp - ,exchange_url - ,total_without_fee - ,wire_fee - ,exchange_sig - ,wire_transfer_deadline - ,signkey_serial - ,account_serial - ) VALUES ( - my_order_serial - ,in_deposit_timestamp - ,in_exchange_url - ,in_total_without_fee - ,in_wire_fee - ,in_exchange_sig - ,in_wire_transfer_deadline - ,my_signkey_serial - ,my_account_serial - ) RETURNING deposit_confirmation_serial - INTO out_deposit_confirmation_serial; - ELSE - IF (in_deposit_timestamp, - in_wire_transfer_deadline, - in_wire_fee, - my_account_serial) - IS DISTINCT FROM - (my_record.deposit_timestamp, - my_record.wire_transfer_deadline, - my_record.wire_fee, - my_record.account_serial) - THEN - out_conflict = TRUE; - out_deposit_confirmation_serial = my_record.deposit_confirmation_serial; - RETURN; - END IF; - IF ( ((in_total_without_fee).val < (my_record.total_without_fee).val) OR - ( ((in_total_without_fee).val = (my_record.total_without_fee).val) AND - ((in_total_without_fee).frac <= (my_record.total_without_fee).frac) ) ) - THEN - out_deposit_confirmation_serial = my_record.deposit_confirmation_serial; - RETURN; - END IF; - UPDATE merchant_deposit_confirmations - SET total_without_fee = in_total_without_fee - ,exchange_sig = in_exchange_sig - ,signkey_serial = my_signkey_serial; - out_deposit_confirmation_serial = my_record.deposit_confirmation_serial; - END IF; - PERFORM pg_notify ('XBZ19D98AK2REYNX93F736A56MT14SCY2EEX7XNXQMNCQ01B121R0', - in_notify_arg_str); - END $BODY$ - $OUTER$, s); - - -- merchant_insert_deposit_to_transfer - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_insert_deposit_to_transfer(in_deposit_serial bigint, in_coin_contribution merchant.taler_amount_currency, in_execution_time bigint, in_exchange_url text, in_h_wire bytea, in_exchange_sig bytea, in_exchange_pub bytea, in_wtid bytea, OUT out_dummy boolean) RETURNS boolean - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_signkey_serial INT8; - my_account_serial INT8; - my_decose INT8; - my_expected_credit_serial INT8; - my_wire_pending_cleared BOOL; - BEGIN - out_dummy=FALSE; - SELECT signkey_serial - INTO my_signkey_serial - FROM merchant.merchant_exchange_signing_keys - WHERE exchange_pub=in_exchange_pub - ORDER BY start_date DESC - LIMIT 1; - IF NOT FOUND - THEN - UPDATE merchant_deposits - SET settlement_last_ec=2029 - ,settlement_last_http_status=200 - ,settlement_last_detail=ENCODE(in_exchange_pub, 'hex') - ,settlement_wtid=in_wtid - ,settlement_retry_needed=TRUE - ,settlement_retry_time=(EXTRACT(epoch FROM (CURRENT_TIME + interval '8 hours')) * 1000000)::INT8 - WHERE deposit_serial=in_deposit_serial; - RETURN; - END IF; - SELECT deposit_confirmation_serial - INTO my_decose - FROM merchant_deposits - WHERE deposit_serial=in_deposit_serial; - SELECT account_serial - INTO my_account_serial - FROM merchant_deposit_confirmations mdc - JOIN merchant_accounts ma - USING (account_serial) - WHERE mdc.deposit_confirmation_serial=my_decose - AND ma.h_wire=in_h_wire; - IF NOT FOUND - THEN - UPDATE merchant_deposits - SET settlement_last_ec=2558 - ,settlement_last_http_status=200 - ,settlement_last_detail=ENCODE(in_h_wire, 'hex') - ,settlement_wtid=in_wtid - ,settlement_retry_needed=FALSE - ,settlement_coin_contribution=in_coin_contribution - ,signkey_serial=my_signkey_serial - ,settlement_exchange_sig=in_exchange_sig - WHERE deposit_serial=in_deposit_serial; - RETURN; - END IF; - SELECT expected_credit_serial - INTO my_expected_credit_serial - FROM merchant_expected_transfers - WHERE wtid=in_wtid - AND exchange_url=in_exchange_url - AND account_serial=my_account_serial; - IF NOT FOUND - THEN - INSERT INTO merchant_expected_transfers - (exchange_url - ,wtid - ,account_serial - ,expected_time) - VALUES - (in_exchange_url - ,in_wtid - ,my_account_serial - ,in_execution_time) - RETURNING expected_credit_serial - INTO my_expected_credit_serial; - END IF; - UPDATE merchant_deposits - SET settlement_last_ec=0 - ,settlement_last_http_status=200 - ,settlement_last_detail=NULL - ,settlement_wtid=in_wtid - ,settlement_retry_needed=FALSE - ,settlement_coin_contribution=in_coin_contribution - ,settlement_expected_credit_serial=my_expected_credit_serial - ,signkey_serial=my_signkey_serial - ,settlement_exchange_sig=in_exchange_sig - WHERE deposit_serial=in_deposit_serial; - NOTIFY XR6849FMRD2AJFY1E2YY0GWA8GN0YT407Z66WHJB0SAKJWF8G2Q60; - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- Tokens - -- ------------------------------------------------------------------- - - -- merchant_do_insert_issued_token - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_insert_issued_token(in_h_issue_pub bytea, in_h_contract_terms bytea, in_blind_sig bytea, OUT out_no_family boolean, OUT out_existed boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_rec RECORD; - my_tfk_serial INT8; - my_tf_serial INT8; - BEGIN - SELECT token_family_key_serial - ,token_family_serial - INTO my_rec - FROM merchant_token_family_keys - WHERE h_pub = in_h_issue_pub; - IF NOT FOUND - THEN - out_no_family = TRUE; - out_existed = FALSE; - return; - END IF; - my_tfk_serial = my_rec.token_family_key_serial; - my_tf_serial = my_rec.token_family_serial; - out_no_family = FALSE; - INSERT INTO merchant_issued_tokens - (token_family_key_serial - ,h_contract_terms - ,blind_sig - ) VALUES - (my_tfk_serial - ,in_h_contract_terms - ,in_blind_sig) - ON CONFLICT DO NOTHING; - IF NOT FOUND - THEN - out_existed = TRUE; - return; - END IF; - out_existed = FALSE; - UPDATE merchant_token_families - SET issued=issued+1 - WHERE token_family_serial=my_tf_serial; - END $BODY$ - $OUTER$, s); - - -- merchant_do_insert_spent_token - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_insert_spent_token(in_h_contract_terms bytea, in_h_issue_pub bytea, in_use_pub bytea, in_use_sig bytea, in_issue_sig bytea, OUT out_no_family boolean, OUT out_conflict boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_rec RECORD; - my_tfk_serial INT8; - my_tf_serial INT8; - BEGIN - SELECT token_family_key_serial - ,token_family_serial - INTO my_rec - FROM merchant_token_family_keys - WHERE h_pub = in_h_issue_pub; - IF NOT FOUND - THEN - out_no_family = TRUE; - out_conflict = FALSE; - return; - END IF; - out_no_family = FALSE; - my_tfk_serial = my_rec.token_family_key_serial; - my_tf_serial = my_rec.token_family_serial; - INSERT INTO merchant_used_tokens - (token_family_key_serial - ,h_contract_terms - ,token_pub - ,token_sig - ,blind_sig - ) VALUES - (my_tfk_serial - ,in_h_contract_terms - ,in_use_pub - ,in_use_sig - ,in_issue_sig) - ON CONFLICT DO NOTHING; - IF NOT FOUND - THEN - PERFORM FROM merchant_used_tokens - WHERE token_family_key_serial=my_tfk_serial - AND h_contract_terms=in_h_contract_terms - AND token_pub=in_use_pub - AND token_sig=in_use_sig - AND blind_sig=in_issue_sig; - out_conflict = NOT FOUND; - return; - END IF; - out_conflict = FALSE; - UPDATE merchant_token_families - SET used=used+1 - WHERE token_family_serial=my_tf_serial; - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- Products and product groups - -- ------------------------------------------------------------------- - - -- merchant_do_insert_product - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_insert_product(in_product_id text, in_description text, in_description_i18n jsonb, in_unit text, in_image text, in_taxes jsonb, ina_price_list merchant.taler_amount_currency[], in_total_stock bigint, in_total_stock_frac integer, in_allow_fractional_quantity boolean, in_fractional_precision_level integer, in_address jsonb, in_next_restock bigint, in_minimum_age integer, ina_categories bigint[], in_product_name text, in_product_group_id bigint, in_money_pot_id bigint, in_price_is_net boolean, OUT out_conflict boolean, OUT out_no_cat bigint, OUT out_no_group boolean, OUT out_no_pot boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_product_serial INT8; - i INT8; - ini_cat INT8; - BEGIN - out_no_group = FALSE; - out_no_pot = FALSE; - IF in_product_group_id IS NOT NULL - THEN - PERFORM FROM merchant_product_groups - WHERE product_group_serial=in_product_group_id; - IF NOT FOUND - THEN - out_no_group=TRUE; - out_conflict=FALSE; - out_no_cat=NULL; - RETURN; - END IF; - END IF; - IF in_money_pot_id IS NOT NULL - THEN - PERFORM FROM merchant_money_pots - WHERE money_pot_serial=in_money_pot_id; - IF NOT FOUND - THEN - out_no_pot=TRUE; - out_conflict=FALSE; - out_no_cat=NULL; - RETURN; - END IF; - END IF; - INSERT INTO merchant_inventory - (product_id - ,product_name - ,description - ,description_i18n - ,unit - ,image - ,image_hash - ,taxes - ,price_array - ,total_stock - ,total_stock_frac - ,allow_fractional_quantity - ,fractional_precision_level - ,address - ,next_restock - ,minimum_age - ,product_group_serial - ,money_pot_serial - ,price_is_net - ) VALUES ( - in_product_id - ,in_product_name - ,in_description - ,in_description_i18n - ,in_unit - ,in_image - ,CASE - WHEN (in_image IS NULL) OR (in_image = '') - THEN NULL - ELSE encode(public.digest(convert_to(in_image, 'UTF8'), - 'sha256'), - 'hex') - END - ,in_taxes - ,ina_price_list - ,in_total_stock - ,in_total_stock_frac - ,in_allow_fractional_quantity - ,in_fractional_precision_level - ,in_address - ,in_next_restock - ,in_minimum_age - ,in_product_group_id - ,in_money_pot_id - ,in_price_is_net - ) - ON CONFLICT (product_id) DO NOTHING - RETURNING product_serial - INTO my_product_serial; - IF NOT FOUND - THEN - SELECT product_serial - INTO my_product_serial - FROM merchant_inventory - WHERE product_id=in_product_id - AND product_name=in_product_name - AND description=in_description - AND description_i18n=in_description_i18n - AND unit=in_unit - AND image=in_image - AND taxes=in_taxes - AND to_jsonb(COALESCE(price_array, ARRAY[]::taler_amount_currency[])) - = to_jsonb(COALESCE(ina_price_list, ARRAY[]::taler_amount_currency[])) - AND total_stock=in_total_stock - AND total_stock_frac=in_total_stock_frac - AND allow_fractional_quantity=in_allow_fractional_quantity - AND fractional_precision_level=in_fractional_precision_level - AND address=in_address - AND next_restock=in_next_restock - AND minimum_age=in_minimum_age - AND product_group_serial IS NOT DISTINCT FROM in_product_group_id - AND money_pot_serial IS NOT DISTINCT FROM in_money_pot_id - AND price_is_net=in_price_is_net; - IF NOT FOUND - THEN - out_conflict=TRUE; - out_no_cat=NULL; - RETURN; - END IF; - FOR i IN 1..COALESCE(array_length(ina_categories,1),0) - LOOP - ini_cat=ina_categories[i]; - PERFORM - FROM merchant_product_categories - WHERE product_serial=my_product_serial - AND category_serial=ini_cat; - IF NOT FOUND - THEN - out_conflict=TRUE; - out_no_cat=NULL; - RETURN; - END IF; - END LOOP; - SELECT COUNT(*) - INTO i - FROM merchant_product_categories - WHERE product_serial=my_product_serial; - IF i != COALESCE(array_length(ina_categories,1),0) - THEN - out_conflict=TRUE; - out_no_cat=NULL; - RETURN; - END IF; - out_conflict=FALSE; - out_no_cat=NULL; - RETURN; - END IF; - out_conflict=FALSE; - FOR i IN 1..COALESCE(array_length(ina_categories,1),0) - LOOP - ini_cat=ina_categories[i]; - INSERT INTO merchant_product_categories - (product_serial - ,category_serial) - VALUES - (my_product_serial - ,ini_cat) - ON CONFLICT DO NOTHING; - IF NOT FOUND - THEN - out_no_cat=i; - RETURN; - END IF; - END LOOP; - out_no_cat=NULL; - END $BODY$ - $OUTER$, s); - - -- merchant_do_update_product - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_update_product(in_product_id text, in_description text, in_description_i18n jsonb, in_unit text, in_image text, in_taxes jsonb, ina_price_list merchant.taler_amount_currency[], in_total_stock bigint, in_total_stock_frac integer, in_allow_fractional_quantity boolean, in_fractional_precision_level integer, in_total_lost bigint, in_address jsonb, in_next_restock bigint, in_minimum_age integer, ina_categories bigint[], in_product_name text, in_product_group_id bigint, in_money_pot_id bigint, in_price_is_net boolean, OUT out_no_product boolean, OUT out_lost_reduced boolean, OUT out_sold_reduced boolean, OUT out_stocked_reduced boolean, OUT out_no_cat bigint, OUT out_no_group boolean, OUT out_no_pot boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_product_serial INT8; - i INT8; - ini_cat INT8; - rec RECORD; - BEGIN - out_no_group = FALSE; - out_no_pot = FALSE; - out_no_product=FALSE; - out_lost_reduced=FALSE; - out_sold_reduced=FALSE; - out_stocked_reduced=FALSE; - out_no_cat=NULL; - IF in_product_group_id IS NOT NULL - THEN - PERFORM FROM merchant_product_groups - WHERE product_group_serial=in_product_group_id; - IF NOT FOUND - THEN - out_no_group=TRUE; - RETURN; - END IF; - END IF; - IF in_money_pot_id IS NOT NULL - THEN - PERFORM FROM merchant_money_pots - WHERE money_pot_serial=in_money_pot_id; - IF NOT FOUND - THEN - out_no_pot=TRUE; - RETURN; - END IF; - END IF; - SELECT total_stock - ,total_stock_frac - ,total_lost - ,allow_fractional_quantity - ,product_serial - INTO rec - FROM merchant_inventory - WHERE product_id=in_product_id; - IF NOT FOUND - THEN - out_no_product=TRUE; - RETURN; - END IF; - my_product_serial = rec.product_serial; - IF rec.total_stock > in_total_stock - THEN - out_stocked_reduced=TRUE; - RETURN; - END IF; - IF rec.total_lost > in_total_lost - THEN - out_lost_reduced=TRUE; - RETURN; - END IF; - IF rec.allow_fractional_quantity - AND (NOT in_allow_fractional_quantity) - THEN - DELETE - FROM merchant_inventory_locks - WHERE product_serial = my_product_serial - AND total_locked_frac <> 0; - END IF; - DELETE FROM merchant_product_categories - WHERE product_serial=my_product_serial; - FOR i IN 1..COALESCE(array_length(ina_categories,1),0) - LOOP - ini_cat=ina_categories[i]; - INSERT INTO merchant_product_categories - (product_serial - ,category_serial) - VALUES - (my_product_serial - ,ini_cat) - ON CONFLICT DO NOTHING; - IF NOT FOUND - THEN - out_no_cat=i; - RETURN; - END IF; - END LOOP; - UPDATE merchant_inventory SET - description=in_description - ,description_i18n=in_description_i18n - ,product_name=in_product_name - ,unit=in_unit - ,image=in_image - ,image_hash=CASE - WHEN (in_image IS NULL) OR (in_image = '') - THEN NULL - ELSE encode(public.digest(convert_to(in_image, 'UTF8'), - 'sha256'), - 'hex') - END - ,taxes=in_taxes - ,price_array=ina_price_list - ,total_stock=in_total_stock - ,total_stock_frac=in_total_stock_frac - ,allow_fractional_quantity=in_allow_fractional_quantity - ,fractional_precision_level=in_fractional_precision_level - ,total_lost=in_total_lost - ,address=in_address - ,next_restock=in_next_restock - ,minimum_age=in_minimum_age - ,product_group_serial=in_product_group_id - ,money_pot_serial=in_money_pot_id - ,price_is_net=in_price_is_net - WHERE product_serial=my_product_serial; - ASSERT FOUND,'SELECTED it earlier, should UPDATE it now'; - END $BODY$ - $OUTER$, s); - - -- merchant_do_update_product_group - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_update_product_group(in_product_group_serial text, in_name text, in_description text, OUT out_conflict boolean, OUT out_not_found boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - BEGIN - BEGIN - UPDATE merchant_product_groups SET - product_group_name=in_name - ,product_group_description=in_description - WHERE product_group_serial=in_product_group_serial; - out_not_found = NOT FOUND; - out_conflict = FALSE; - RETURN; - EXCEPTION - WHEN unique_violation - THEN - out_conflict = TRUE; - RETURN; - END; - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- Transfers - -- ------------------------------------------------------------------- - - -- merchant_do_insert_transfer - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_insert_transfer(in_exchange_url text, in_wtid bytea, in_credit_amount merchant.taler_amount_currency, in_credited_account_payto text, in_bank_serial_id bigint, in_execution_time bigint, OUT out_no_account boolean, OUT out_conflict boolean) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_account_serial INT8; - my_record RECORD; - my_bank_serial_id INT8; - my_credit_amount taler_amount_currency; - BEGIN - out_conflict=FALSE; - SELECT account_serial - INTO my_account_serial - FROM merchant_accounts - WHERE REGEXP_REPLACE(payto_uri, - '\\?.*','') - =REGEXP_REPLACE(in_credited_account_payto, - '\\?.*',''); - IF NOT FOUND - THEN - out_no_account=TRUE; - RETURN; - END IF; - out_no_account=FALSE; - SELECT bank_serial_id - ,credit_amount - INTO my_record - FROM merchant_transfers - WHERE wtid=in_wtid - AND account_serial=my_account_serial - AND exchange_url=in_exchange_url; - IF NOT FOUND - THEN - INSERT INTO merchant_transfers - (exchange_url - ,wtid - ,credit_amount - ,account_serial - ,bank_serial_id - ,execution_time - ) VALUES - (in_exchange_url - ,in_wtid - ,in_credit_amount - ,my_account_serial - ,in_bank_serial_id - ,in_execution_time); - NOTIFY XJ5N652FA4TBS2WXGY3S1FMPMQYTD8KAZA9B7HW9JWJ4PZ2DB852G; - RETURN; - END IF; - my_bank_serial_id = my_record.bank_serial_id; - my_credit_amount = my_record.credit_amount; - IF ( (in_credit_amount.val != my_credit_amount.val) OR - (in_credit_amount.frac != my_credit_amount.frac) OR - (in_credit_amount.curr != my_credit_amount.curr) ) - THEN - out_conflict = TRUE; - RETURN; - END IF; - IF ( (my_bank_serial_id IS NULL) AND - (in_bank_serial_id IS NOT NULL) ) - THEN - UPDATE merchant_transfers - SET bank_serial_id=in_bank_serial_id - WHERE wtid=in_wtid - AND account_serial=my_account_serial - AND exchange_url=in_exchange_url; - RETURN; - END IF; - END $BODY$ - $OUTER$, s); - - -- merchant_do_insert_transfer_details - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_insert_transfer_details(in_exchange_url text, in_payto_uri text, in_wtid bytea, in_execution_time bigint, in_exchange_pub bytea, in_exchange_sig bytea, in_total_amount merchant.taler_amount_currency, in_wire_fee merchant.taler_amount_currency, ina_coin_values merchant.taler_amount_currency[], ina_deposit_fees merchant.taler_amount_currency[], ina_coin_pubs bytea[], ina_contract_terms bytea[], OUT out_no_account boolean, OUT out_no_exchange boolean, OUT out_duplicate boolean, OUT out_conflict boolean, OUT out_order_id text) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_signkey_serial INT8; - my_expected_credit_serial INT8; - my_affected_orders RECORD; - my_decose INT8; - my_order_id TEXT; - i INT8; - curs CURSOR (arg_coin_pub BYTEA) FOR - SELECT mcon.deposit_confirmation_serial, - mcon.order_serial - FROM merchant_deposits dep - JOIN merchant_deposit_confirmations mcon - USING (deposit_confirmation_serial) - WHERE dep.coin_pub=arg_coin_pub; - ini_coin_pub BYTEA; - ini_contract_term BYTEA; - ini_coin_value taler_amount_currency; - ini_deposit_fee taler_amount_currency; - BEGIN - SELECT expected_credit_serial - INTO my_expected_credit_serial - FROM merchant_expected_transfers - WHERE exchange_url=in_exchange_url - AND wtid=in_wtid - AND account_serial= - (SELECT account_serial - FROM merchant_accounts - WHERE payto_uri=in_payto_uri - AND exchange_url=in_exchange_url); - IF NOT FOUND - THEN - out_no_account=TRUE; - out_no_exchange=FALSE; - out_duplicate=FALSE; - out_conflict=FALSE; - RETURN; - END IF; - out_no_account=FALSE; - SELECT signkey_serial - INTO my_signkey_serial - FROM merchant.merchant_exchange_signing_keys - WHERE exchange_pub=in_exchange_pub - ORDER BY start_date DESC - LIMIT 1; - IF NOT FOUND - THEN - out_no_exchange=TRUE; - out_conflict=FALSE; - out_duplicate=FALSE; - RETURN; - END IF; - out_no_exchange=FALSE; - INSERT INTO merchant_transfer_signatures - (expected_credit_serial - ,signkey_serial - ,credit_amount - ,wire_fee - ,execution_time - ,exchange_sig) - VALUES - (my_expected_credit_serial - ,my_signkey_serial - ,in_total_amount - ,in_wire_fee - ,in_execution_time - ,in_exchange_sig) - ON CONFLICT DO NOTHING; - IF NOT FOUND - THEN - PERFORM 1 - FROM merchant_transfer_signatures - WHERE expected_credit_serial=my_expected_credit_serial - AND signkey_serial=my_signkey_serial - AND credit_amount=in_total_amount - AND wire_fee=in_wire_fee - AND execution_time=in_execution_time - AND exchange_sig=in_exchange_sig; - IF FOUND - THEN - out_duplicate=TRUE; - out_conflict=FALSE; - RETURN; - END IF; - out_duplicate=FALSE; - out_conflict=TRUE; - RETURN; - END IF; - out_duplicate=FALSE; - out_conflict=FALSE; - FOR i IN 1..array_length(ina_coin_pubs,1) - LOOP - ini_coin_value=ina_coin_values[i]; - ini_deposit_fee=ina_deposit_fees[i]; - ini_coin_pub=ina_coin_pubs[i]; - ini_contract_term=ina_contract_terms[i]; - INSERT INTO merchant_expected_transfer_to_coin - (deposit_serial - ,expected_credit_serial - ,offset_in_exchange_list - ,exchange_deposit_value - ,exchange_deposit_fee) - SELECT - dep.deposit_serial - ,my_expected_credit_serial - ,i - ,ini_coin_value - ,ini_deposit_fee - FROM merchant_deposits dep - JOIN merchant_deposit_confirmations dcon - USING (deposit_confirmation_serial) - JOIN merchant_contract_terms cterm - USING (order_serial) - WHERE dep.coin_pub=ini_coin_pub - AND cterm.h_contract_terms=ini_contract_term; - RAISE NOTICE 'iterating over affected orders'; - OPEN curs (arg_coin_pub:=ini_coin_pub); - LOOP - FETCH NEXT FROM curs INTO my_affected_orders; - EXIT WHEN NOT FOUND; - RAISE NOTICE 'checking affected order for completion'; - my_decose=my_affected_orders.deposit_confirmation_serial; - PERFORM FROM merchant_deposits md - WHERE md.deposit_confirmation_serial=my_decose - AND settlement_retry_needed - OR settlement_wtid IS NULL; - IF NOT FOUND - THEN - UPDATE merchant_deposit_confirmations - SET wire_pending=FALSE - WHERE (deposit_confirmation_serial=my_decose); - IF FOUND - THEN - RAISE NOTICE 'checking affected contract for completion'; - PERFORM FROM merchant_deposit_confirmations mdc - WHERE mdc.wire_pending - AND mdc.order_serial=my_affected_orders.order_serial; - IF NOT FOUND - THEN - UPDATE merchant_contract_terms - SET wired=TRUE - WHERE (order_serial=my_affected_orders.order_serial); - SELECT order_id - INTO my_order_id - FROM merchant_contract_terms - WHERE order_serial=my_affected_orders.order_serial; - out_order_id = my_order_id; - INSERT INTO merchant_pending_webhooks - (webhook_serial - ,url - ,http_method - ,header - ,body) - SELECT mw.webhook_serial - ,mw.url - ,mw.http_method - ,merchant.replace_placeholder( - merchant.replace_placeholder(mw.header_template, 'order_id', my_order_id), - 'wtid', encode(in_wtid, 'hex') - )::TEXT - ,merchant.replace_placeholder( - merchant.replace_placeholder(mw.body_template, 'order_id', my_order_id), - 'wtid', encode(in_wtid, 'hex') - )::TEXT - FROM merchant_webhook mw - WHERE mw.event_type = 'order_settled'; - IF FOUND - THEN - NOTIFY XXJWF6C1DCS1255RJH7GQ1EK16J8DMRSQ6K9EDKNKCP7HRVWAJPKG; - END IF; - END IF; - END IF; - END IF; - END LOOP; - CLOSE curs; - END LOOP; - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- TAN / MFA challenges - -- ------------------------------------------------------------------- - - -- merchant_do_solve_mfa_challenge - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_do_solve_mfa_challenge(in_challenge_id bigint, in_h_body bytea, in_solution text, in_now bigint, OUT out_solved boolean, OUT out_retry_counter integer) RETURNS record - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_confirmation_date INT8; - DECLARE - my_rec RECORD; - BEGIN - SELECT - tc.confirmation_date - ,tc.retry_counter - ,(tc.code = in_solution) AS solved - INTO - my_rec - FROM tan_challenges tc - WHERE tc.challenge_id = in_challenge_id - AND tc.h_body = in_h_body - AND tc.expiration_date > in_now; - IF NOT FOUND - THEN - out_solved = FALSE; - RETURN; - END IF; - my_confirmation_date = my_rec.confirmation_date; - out_retry_counter = my_rec.retry_counter; - out_solved = my_rec.solved; - IF my_confirmation_date IS NOT NULL - THEN - out_solved = TRUE; - RETURN; - END IF; - IF (0 = out_retry_counter) - THEN - out_solved = FALSE; - RETURN; - END IF; - IF out_solved - THEN - my_confirmation_date = in_now; - UPDATE tan_challenges - SET confirmation_date = my_confirmation_date - WHERE challenge_id = in_challenge_id; - ELSE - out_retry_counter = out_retry_counter - 1; - UPDATE tan_challenges - SET retry_counter = out_retry_counter - WHERE challenge_id = in_challenge_id; - END IF; - END; - $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- Statistic interval getters - -- ------------------------------------------------------------------- - - -- merchant_statistic_interval_amount_get - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_statistic_interval_amount_get(in_slug text) RETURNS SETOF merchant.merchant_statistic_interval_amount_get_return_value - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_time INT8 DEFAULT ROUND(EXTRACT(epoch FROM CURRENT_TIMESTAMP(0)::TIMESTAMP) * 1000000)::INT8 / 1000 / 1000; - my_ranges INT8[]; - my_range INT8; - my_delta_value INT8; - my_delta_frac INT8; - my_meta INT8; - my_next_max_serial INT8; - my_currency TEXT; - my_rec RECORD; - my_irec RECORD; - my_jrec RECORD; - my_i INT; - my_min_serial INT8 DEFAULT NULL; - my_rval merchant.merchant_statistic_interval_amount_get_return_value; - BEGIN - SELECT imeta_serial_id - ,ranges - ,precisions - INTO my_rec - FROM merchant_statistic_interval_meta - WHERE slug=in_slug; - IF NOT FOUND - THEN - RETURN; - END IF; - my_meta = my_rec.imeta_serial_id; - my_ranges = my_rec.ranges; - FOR my_currency IN - SELECT DISTINCT delta_curr - FROM merchant_statistic_amount_event - WHERE imeta_serial_id = my_meta - LOOP - my_rval.rvalue.val = 0; - my_rval.rvalue.frac = 0; - my_rval.rvalue.curr = my_currency; - FOR my_i IN 1..COALESCE(array_length(my_ranges,1),0) - LOOP - my_range = my_ranges[my_i]; - SELECT event_delimiter - ,cumulative_value - ,cumulative_frac - INTO my_irec - FROM merchant_statistic_interval_amount - WHERE imeta_serial_id = my_meta - AND curr = my_currency - AND range = my_range; - IF FOUND - THEN - my_min_serial = my_irec.event_delimiter; - my_rval.rvalue.val = (my_rval.rvalue).val + my_irec.cumulative_value + my_irec.cumulative_frac / 100000000; - my_rval.rvalue.frac = (my_rval.rvalue).frac + my_irec.cumulative_frac %% 100000000; - IF (my_rval.rvalue).frac > 100000000 - THEN - my_rval.rvalue.frac = (my_rval.rvalue).frac - 100000000; - my_rval.rvalue.val = (my_rval.rvalue).val + 1; - END IF; - SELECT SUM(delta_value) AS value_sum - ,SUM(delta_frac) AS frac_sum - INTO my_jrec - FROM merchant_statistic_amount_event - WHERE imeta_serial_id = my_meta - AND delta_curr = my_currency - AND slot < my_time - my_range - AND aevent_serial_id >= my_min_serial; - IF FOUND AND my_jrec.value_sum IS NOT NULL - THEN - my_delta_value = my_jrec.value_sum + my_jrec.frac_sum / 100000000; - my_delta_frac = my_jrec.frac_sum %% 100000000; - my_rval.rvalue.val = (my_rval.rvalue).val - my_delta_value; - IF ((my_rval.rvalue).frac >= my_delta_frac) - THEN - my_rval.rvalue.frac = (my_rval.rvalue).frac - my_delta_frac; - ELSE - my_rval.rvalue.frac = 100000000 + (my_rval.rvalue).frac - my_delta_frac; - my_rval.rvalue.val = (my_rval.rvalue).val - 1; - END IF; - SELECT aevent_serial_id - INTO my_next_max_serial - FROM merchant_statistic_amount_event - WHERE imeta_serial_id = my_meta - AND delta_curr = my_currency - AND slot >= my_time - my_range - AND aevent_serial_id >= my_min_serial - ORDER BY slot ASC - LIMIT 1; - IF FOUND - THEN - UPDATE merchant_statistic_interval_amount SET - cumulative_value = cumulative_value - my_delta_value - - CASE - WHEN cumulative_frac < my_delta_frac - THEN 1 - ELSE 0 - END, - cumulative_frac = cumulative_frac - my_delta_frac - + CASE - WHEN cumulative_frac < my_delta_frac - THEN 100000000 - ELSE 0 - END, - event_delimiter = my_next_max_serial - WHERE imeta_serial_id = my_meta - AND curr = my_currency - AND range = my_range; - ELSE - DELETE FROM merchant_statistic_interval_amount - WHERE imeta_serial_id = my_meta - AND curr = my_currency - AND range = my_range; - END IF; - IF (my_i < COALESCE(array_length(my_ranges,1),0)) - THEN - UPDATE merchant_statistic_interval_amount AS msia SET - cumulative_value = cumulative_value + my_delta_value - + CASE - WHEN cumulative_frac + my_delta_frac > 100000000 - THEN 1 - ELSE 0 - END, - cumulative_frac = cumulative_frac + my_delta_frac - - CASE - WHEN cumulative_frac + my_delta_frac > 100000000 - THEN 100000000 - ELSE 0 - END, - event_delimiter = LEAST (msia.event_delimiter,my_min_serial) - WHERE imeta_serial_id = my_meta - AND range=my_ranges[my_i+1]; - IF NOT FOUND - THEN - INSERT INTO merchant_statistic_interval_amount - (imeta_serial_id - ,event_delimiter - ,range - ,curr - ,cumulative_value - ,cumulative_frac - ) VALUES ( - my_meta - ,my_min_serial - ,my_ranges[my_i+1] - ,my_currency - ,my_delta_value - ,my_delta_frac); - END IF; - ELSE - DELETE FROM merchant_statistic_amount_event - WHERE imeta_serial_id = my_meta - AND slot < my_time - my_range; - END IF; - END IF; - my_rval.range = my_range; - RETURN NEXT my_rval; - END IF; - END LOOP; - END LOOP; - END $BODY$ - $OUTER$, s); - - -- merchant_statistic_interval_number_get - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_statistic_interval_number_get(in_slug text) RETURNS SETOF merchant.merchant_statistic_interval_number_get_return_value - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_time INT8 DEFAULT ROUND(EXTRACT(epoch FROM CURRENT_TIMESTAMP(0)::TIMESTAMP) * 1000000)::INT8 / 1000 / 1000; - my_ranges INT8[]; - my_range INT8; - my_delta INT8; - my_meta INT8; - my_next_max_serial INT8; - my_rec RECORD; - my_irec RECORD; - my_i INT; - my_min_serial INT8 DEFAULT NULL; - my_rval merchant.merchant_statistic_interval_number_get_return_value; - BEGIN - SELECT imeta_serial_id - ,ranges - ,precisions - INTO my_rec - FROM merchant_statistic_interval_meta - WHERE slug=in_slug; - IF NOT FOUND - THEN - RETURN; - END IF; - my_rval.rvalue = 0; - my_ranges = my_rec.ranges; - my_meta = my_rec.imeta_serial_id; - FOR my_i IN 1..COALESCE(array_length(my_ranges,1),0) - LOOP - my_range = my_ranges[my_i]; - SELECT event_delimiter - ,cumulative_number - INTO my_irec - FROM merchant_statistic_interval_counter - WHERE imeta_serial_id = my_meta - AND range = my_range; - IF FOUND - THEN - my_min_serial = my_irec.event_delimiter; - my_rval.rvalue = my_rval.rvalue + my_irec.cumulative_number; - SELECT SUM(delta) AS delta_sum - INTO my_irec - FROM merchant_statistic_counter_event - WHERE imeta_serial_id = my_meta - AND slot < my_time - my_range - AND nevent_serial_id >= my_min_serial; - IF FOUND AND my_irec.delta_sum IS NOT NULL - THEN - my_delta = my_irec.delta_sum; - my_rval.rvalue = my_rval.rvalue - my_delta; - SELECT nevent_serial_id - INTO my_next_max_serial - FROM merchant_statistic_counter_event - WHERE imeta_serial_id = my_meta - AND slot >= my_time - my_range - AND nevent_serial_id >= my_min_serial - ORDER BY slot ASC - LIMIT 1; - IF FOUND - THEN - UPDATE merchant_statistic_interval_counter - SET cumulative_number = cumulative_number - my_delta, - event_delimiter = my_next_max_serial - WHERE imeta_serial_id = my_meta - AND range = my_range; - ELSE - DELETE FROM merchant_statistic_interval_counter - WHERE imeta_serial_id = my_meta - AND range = my_range; - END IF; - IF (my_i < COALESCE(array_length(my_ranges,1),0)) - THEN - UPDATE merchant_statistic_interval_counter AS usic SET - cumulative_number = cumulative_number + my_delta, - event_delimiter = LEAST(usic.event_delimiter,my_min_serial) - WHERE imeta_serial_id = my_meta - AND range=my_ranges[my_i+1]; - IF NOT FOUND - THEN - INSERT INTO merchant_statistic_interval_counter - (imeta_serial_id - ,range - ,event_delimiter - ,cumulative_number - ) VALUES ( - my_meta - ,my_ranges[my_i+1] - ,my_min_serial - ,my_delta); - END IF; - ELSE - DELETE FROM merchant_statistic_counter_event - WHERE imeta_serial_id = my_meta - AND slot < my_time - my_range; - END IF; - END IF; - my_rval.range = my_range; - RETURN NEXT my_rval; - END IF; - END LOOP; - END $BODY$ - $OUTER$, s); diff --git a/src/backenddb/pg_create_instance_schema_tables.sql.fragment b/src/backenddb/pg_create_instance_schema_tables.sql.fragment @@ -1,961 +0,0 @@ --- ===================================================================== --- Per-instance schema constructor: tables, indexes, FKs. --- --- This fragment is embedded inside a PL/pgSQL function whose local --- variable `s TEXT` already holds the schema name string --- 'merchant_instance_<merchant_serial>'. Every DDL is issued via --- EXECUTE format(...) with %I quoting on the schema name(s). --- --- Triggers, sequence resets, stored procedures and the merchant.* FK --- back-references to merchant_instances are intentionally omitted. --- ===================================================================== - - -- ------------------------------------------------------------------- - -- 1. Create the per-instance schema. - -- ------------------------------------------------------------------- - EXECUTE format('CREATE SCHEMA %I', s); - - -- ------------------------------------------------------------------- - -- 2. CREATE TABLE statements for all per-instance tables. - -- The merchant_serial column has been dropped everywhere; PK, - -- UNIQUE and CHECK constraints have been adjusted accordingly. - -- ------------------------------------------------------------------- - - -- merchant_accounts - EXECUTE format('CREATE TABLE %I.merchant_accounts (' - || ' account_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' h_wire bytea NOT NULL UNIQUE,' - || ' salt bytea NOT NULL,' - || ' credit_facade_url text,' - || ' credit_facade_credentials jsonb,' - || ' last_bank_serial bigint DEFAULT 0 NOT NULL,' - || ' payto_uri text NOT NULL UNIQUE,' - || ' active boolean NOT NULL,' - || ' extra_wire_subject_metadata text,' - || ' CONSTRAINT merchant_accounts_h_wire_check CHECK ((length(h_wire) = 64)),' - || ' CONSTRAINT merchant_accounts_salt_check CHECK ((length(salt) = 16))' - || ')', s); - - -- merchant_builtin_unit_overrides - EXECUTE format('CREATE TABLE %I.merchant_builtin_unit_overrides (' - || ' builtin_unit_serial bigint NOT NULL PRIMARY KEY,' - || ' override_allow_fraction boolean,' - || ' override_precision_level integer,' - || ' override_active boolean,' - || ' CONSTRAINT merchant_builtin_unit_overrides_override_precision_level_check' - || ' CHECK (((override_precision_level >= 0) AND (override_precision_level <= 6)))' - || ')', s); - - -- merchant_categories - EXECUTE format('CREATE TABLE %I.merchant_categories (' - || ' category_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' category_name text NOT NULL UNIQUE,' - || ' category_name_i18n jsonb NOT NULL' - || ')', s); - - -- merchant_contract_terms - EXECUTE format('CREATE TABLE %I.merchant_contract_terms (' - || ' order_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' order_id text NOT NULL UNIQUE,' - || ' contract_terms jsonb NOT NULL,' - || ' wallet_data text,' - || ' h_contract_terms bytea NOT NULL UNIQUE,' - || ' creation_time bigint NOT NULL,' - || ' pay_deadline bigint NOT NULL,' - || ' refund_deadline bigint NOT NULL,' - || ' paid boolean DEFAULT false NOT NULL,' - || ' wired boolean DEFAULT false NOT NULL,' - || ' fulfillment_url text,' - || $DDL$ session_id text DEFAULT ''::text NOT NULL,$DDL$ - || ' pos_key text,' - || ' pos_algorithm integer DEFAULT 0 NOT NULL,' - || ' claim_token bytea NOT NULL,' - || ' choice_index smallint,' - || ' CONSTRAINT merchant_contract_terms_claim_token_check CHECK ((length(claim_token) = 16)),' - || ' CONSTRAINT merchant_contract_terms_h_contract_terms_check CHECK ((length(h_contract_terms) = 64))' - || ')', s); - - -- merchant_custom_units - EXECUTE format($DDL$CREATE TABLE %I.merchant_custom_units ( - unit_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - unit text NOT NULL UNIQUE, - unit_name_long text NOT NULL, - unit_name_short text NOT NULL, - unit_name_long_i18n bytea DEFAULT convert_to('{}'::text, 'UTF8'::name) NOT NULL, - unit_name_short_i18n bytea DEFAULT convert_to('{}'::text, 'UTF8'::name) NOT NULL, - unit_allow_fraction boolean DEFAULT false NOT NULL, - unit_precision_level integer DEFAULT 0 NOT NULL, - unit_active boolean DEFAULT true NOT NULL, - CONSTRAINT merchant_custom_units_unit_precision_level_check - CHECK (((unit_precision_level >= 0) AND (unit_precision_level <= 6))) - )$DDL$, s); - - -- merchant_deposit_confirmations - EXECUTE format('CREATE TABLE %I.merchant_deposit_confirmations (' - || ' deposit_confirmation_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' order_serial bigint,' - || ' deposit_timestamp bigint NOT NULL,' - || ' exchange_url text NOT NULL,' - || ' total_without_fee merchant.taler_amount_currency NOT NULL,' - || ' wire_fee merchant.taler_amount_currency NOT NULL,' - || ' signkey_serial bigint NOT NULL,' - || ' exchange_sig bytea NOT NULL,' - || ' account_serial bigint NOT NULL,' - || ' wire_transfer_deadline bigint DEFAULT 0 NOT NULL,' - || ' wire_pending boolean DEFAULT true NOT NULL,' - || ' exchange_failure text,' - || ' retry_backoff bigint DEFAULT 0 NOT NULL,' - || ' CONSTRAINT merchant_deposit_confirmations_exchange_sig_check CHECK ((length(exchange_sig) = 64)),' - || ' UNIQUE (order_serial, exchange_sig)' - || ')', s); - - -- merchant_deposits - EXECUTE format('CREATE TABLE %I.merchant_deposits (' - || ' deposit_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' coin_offset integer NOT NULL,' - || ' deposit_confirmation_serial bigint NOT NULL,' - || ' coin_pub bytea NOT NULL,' - || ' coin_sig bytea NOT NULL,' - || ' amount_with_fee merchant.taler_amount_currency NOT NULL,' - || ' deposit_fee merchant.taler_amount_currency NOT NULL,' - || ' refund_fee merchant.taler_amount_currency NOT NULL,' - || ' settlement_retry_needed boolean DEFAULT true,' - || ' settlement_retry_time bigint DEFAULT 0,' - || ' settlement_last_http_status integer,' - || ' settlement_last_ec integer,' - || ' settlement_last_detail text,' - || ' settlement_wtid bytea,' - || ' settlement_coin_contribution merchant.taler_amount_currency,' - || ' settlement_expected_credit_serial bigint,' - || ' signkey_serial bigint,' - || ' settlement_exchange_sig bytea,' - || ' CONSTRAINT merchant_deposits_coin_pub_check CHECK ((length(coin_pub) = 32)),' - || ' CONSTRAINT merchant_deposits_coin_sig_check CHECK ((length(coin_sig) = 64)),' - || ' CONSTRAINT merchant_deposits_settlement_exchange_sig_check CHECK ((length(settlement_exchange_sig) = 64)),' - || ' CONSTRAINT merchant_deposits_settlement_wtid_check CHECK ((length(settlement_wtid) = 32)),' - || ' UNIQUE (deposit_confirmation_serial, coin_pub)' - || ')', s); - - -- merchant_donau_instances - EXECUTE format('CREATE TABLE %I.merchant_donau_instances (' - || ' donau_instances_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' donau_url text NOT NULL,' - || ' charity_name text NOT NULL,' - || ' charity_id bigint NOT NULL,' - || ' charity_max_per_year merchant.taler_amount_currency NOT NULL,' - || ' charity_receipts_to_date merchant.taler_amount_currency NOT NULL,' - || ' current_year bigint NOT NULL,' - || ' UNIQUE (donau_url, charity_id)' - || ')', s); - - -- merchant_expected_transfer_to_coin - EXECUTE format('CREATE TABLE %I.merchant_expected_transfer_to_coin (' - || ' deposit_serial bigint NOT NULL UNIQUE,' - || ' expected_credit_serial bigint NOT NULL,' - || ' offset_in_exchange_list bigint NOT NULL,' - || ' exchange_deposit_value merchant.taler_amount_currency NOT NULL,' - || ' exchange_deposit_fee merchant.taler_amount_currency NOT NULL' - || ')', s); - - -- merchant_expected_transfers - EXECUTE format('CREATE TABLE %I.merchant_expected_transfers (' - || ' expected_credit_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' exchange_url text NOT NULL,' - || ' wtid bytea NOT NULL,' - || ' expected_credit_amount merchant.taler_amount_currency,' - || ' wire_fee merchant.taler_amount_currency,' - || ' account_serial bigint NOT NULL,' - || ' expected_time bigint NOT NULL,' - || ' retry_time bigint DEFAULT 0 NOT NULL,' - || ' last_http_status integer,' - || ' last_ec integer,' - || ' last_detail text,' - || ' retry_needed boolean DEFAULT true NOT NULL,' - || ' signkey_serial bigint,' - || ' exchange_sig bytea,' - || ' h_details bytea,' - || ' confirmed boolean DEFAULT false NOT NULL,' - || ' CONSTRAINT merchant_expected_transfers_exchange_sig_check CHECK ((length(exchange_sig) = 64)),' - || ' CONSTRAINT merchant_expected_transfers_h_details_check CHECK ((length(h_details) = 64)),' - || ' CONSTRAINT merchant_expected_transfers_wtid_check CHECK ((length(wtid) = 32)),' - || ' UNIQUE (wtid, exchange_url, account_serial)' - || ')', s); - - -- merchant_inventory - EXECUTE format($DDL$CREATE TABLE %I.merchant_inventory ( - product_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - product_id text NOT NULL UNIQUE, - description text NOT NULL, - description_i18n jsonb NOT NULL, - unit text NOT NULL, - image text NOT NULL, - taxes jsonb NOT NULL, - total_stock bigint NOT NULL, - total_sold bigint DEFAULT 0 NOT NULL, - total_lost bigint DEFAULT 0 NOT NULL, - address jsonb NOT NULL, - next_restock bigint NOT NULL, - minimum_age integer DEFAULT 0 NOT NULL, - product_name text NOT NULL, - image_hash text, - price_array merchant.taler_amount_currency[] - DEFAULT ARRAY[]::merchant.taler_amount_currency[] NOT NULL, - total_stock_frac integer DEFAULT 0 NOT NULL, - total_sold_frac integer DEFAULT 0 NOT NULL, - total_lost_frac integer DEFAULT 0 NOT NULL, - allow_fractional_quantity boolean DEFAULT false NOT NULL, - fractional_precision_level integer DEFAULT 0 NOT NULL, - product_group_serial bigint, - money_pot_serial bigint, - price_is_net boolean DEFAULT false - )$DDL$, s); - - -- merchant_inventory_locks - EXECUTE format('CREATE TABLE %I.merchant_inventory_locks (' - || ' product_serial bigint NOT NULL,' - || ' lock_uuid bytea NOT NULL,' - || ' total_locked bigint NOT NULL,' - || ' expiration bigint NOT NULL,' - || ' total_locked_frac integer DEFAULT 0 NOT NULL,' - || ' CONSTRAINT merchant_inventory_locks_lock_uuid_check CHECK ((length(lock_uuid) = 16))' - || ')', s); - - -- merchant_issued_tokens - EXECUTE format('CREATE TABLE %I.merchant_issued_tokens (' - || ' issued_token_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' h_contract_terms bytea NOT NULL,' - || ' token_family_key_serial bigint,' - || ' blind_sig bytea NOT NULL,' - || ' CONSTRAINT merchant_issued_tokens_h_contract_terms_check CHECK ((length(h_contract_terms) = 64))' - || ')', s); - - -- merchant_keys (single-row per schema; merchant_priv becomes the PK) - EXECUTE format('CREATE TABLE %I.merchant_keys (' - || ' merchant_priv bytea NOT NULL PRIMARY KEY,' - || ' CONSTRAINT merchant_keys_merchant_priv_check CHECK ((length(merchant_priv) = 32))' - || ')', s); - - -- merchant_kyc (PK survives unchanged: (account_serial, exchange_url)) - EXECUTE format('CREATE TABLE %I.merchant_kyc (' - || ' kyc_serial_id bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,' - || ' kyc_timestamp bigint NOT NULL,' - || ' kyc_ok boolean DEFAULT false NOT NULL,' - || ' account_serial bigint NOT NULL,' - || ' exchange_url text NOT NULL,' - || ' access_token bytea,' - || ' exchange_http_status integer DEFAULT 0,' - || ' exchange_ec_code integer DEFAULT 0,' - || ' aml_review boolean DEFAULT false,' - || ' jaccount_limits jsonb,' - || ' last_rule_gen bigint DEFAULT 0 NOT NULL,' - || ' next_kyc_poll bigint DEFAULT 0 NOT NULL,' - || ' kyc_backoff bigint DEFAULT 0 NOT NULL,' - || ' CONSTRAINT access_token_length_check CHECK ((length(access_token) = 32)),' - || ' PRIMARY KEY (account_serial, exchange_url)' - || ')', s); - - -- merchant_login_tokens (note: serial is GENERATED ALWAYS) - EXECUTE format('CREATE TABLE %I.merchant_login_tokens (' - || ' token bytea NOT NULL UNIQUE,' - || ' creation_time bigint NOT NULL,' - || ' expiration_time bigint NOT NULL,' - || ' validity_scope integer NOT NULL,' - || ' description text NOT NULL,' - || ' serial bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,' - || ' CONSTRAINT merchant_login_tokens_token_check CHECK ((length(token) = 32))' - || ')', s); - - -- merchant_money_pots - EXECUTE format('CREATE TABLE %I.merchant_money_pots (' - || ' money_pot_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' money_pot_name text NOT NULL UNIQUE,' - || ' money_pot_description text NOT NULL,' - || ' pot_totals merchant.taler_amount_currency[]' - || ' DEFAULT ARRAY[]::merchant.taler_amount_currency[] NOT NULL' - || ')', s); - - -- merchant_order_locks - EXECUTE format('CREATE TABLE %I.merchant_order_locks (' - || ' product_serial bigint NOT NULL,' - || ' total_locked bigint NOT NULL,' - || ' order_serial bigint NOT NULL,' - || ' total_locked_frac integer DEFAULT 0 NOT NULL' - || ')', s); - - -- merchant_order_token_blinded_sigs - EXECUTE format('CREATE TABLE %I.merchant_order_token_blinded_sigs (' - || ' order_token_bs_serial bigint GENERATED BY DEFAULT AS IDENTITY,' - || ' order_serial bigint NOT NULL,' - || ' token_index integer NOT NULL,' - || ' token_blinded_signature bytea NOT NULL,' - || ' token_hash bytea NOT NULL,' - || ' CONSTRAINT merchant_order_token_blinded_sigs_token_hash_check CHECK ((length(token_hash) = 64)),' - || ' PRIMARY KEY (order_serial, token_index)' - || ')', s); - - -- merchant_orders - EXECUTE format($DDL$CREATE TABLE %I.merchant_orders ( - order_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - order_id text NOT NULL UNIQUE, - claim_token bytea NOT NULL, - h_post_data bytea NOT NULL, - pay_deadline bigint NOT NULL, - creation_time bigint NOT NULL, - contract_terms jsonb NOT NULL, - pos_key text, - pos_algorithm integer DEFAULT 0 NOT NULL, - fulfillment_url text, - session_id text DEFAULT ''::text NOT NULL, - CONSTRAINT merchant_orders_claim_token_check CHECK ((length(claim_token) = 16)), - CONSTRAINT merchant_orders_h_post_data_check CHECK ((length(h_post_data) = 64)) - )$DDL$, s); - - -- merchant_otp_devices - EXECUTE format('CREATE TABLE %I.merchant_otp_devices (' - || ' otp_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' otp_id text NOT NULL UNIQUE,' - || ' otp_description text NOT NULL,' - || ' otp_key text,' - || ' otp_algorithm integer DEFAULT 0 NOT NULL,' - || ' otp_ctr bigint DEFAULT 0 NOT NULL' - || ')', s); - - -- merchant_pending_webhooks - -- The original (merchant_serial, webhook_pending_serial) UNIQUE - -- collapses to just (webhook_pending_serial), which is already the - -- PK, so no extra UNIQUE constraint is needed. - EXECUTE format('CREATE TABLE %I.merchant_pending_webhooks (' - || ' webhook_pending_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' webhook_serial bigint NOT NULL,' - || ' next_attempt bigint DEFAULT 0 NOT NULL,' - || ' retries integer DEFAULT 0 NOT NULL,' - || ' url text NOT NULL,' - || ' http_method text NOT NULL,' - || ' header text,' - || ' body text' - || ')', s); - - -- merchant_product_categories - EXECUTE format('CREATE TABLE %I.merchant_product_categories (' - || ' category_serial bigint NOT NULL,' - || ' product_serial bigint NOT NULL' - || ')', s); - - -- merchant_product_groups - EXECUTE format('CREATE TABLE %I.merchant_product_groups (' - || ' product_group_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' product_group_name text NOT NULL UNIQUE,' - || ' product_group_description text NOT NULL' - || ')', s); - - -- merchant_refund_proofs - EXECUTE format('CREATE TABLE %I.merchant_refund_proofs (' - || ' refund_serial bigint NOT NULL PRIMARY KEY,' - || ' exchange_sig bytea NOT NULL,' - || ' signkey_serial bigint NOT NULL,' - || ' CONSTRAINT merchant_refund_proofs_exchange_sig_check CHECK ((length(exchange_sig) = 64))' - || ')', s); - - -- merchant_refunds - EXECUTE format('CREATE TABLE %I.merchant_refunds (' - || ' refund_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' order_serial bigint NOT NULL,' - || ' rtransaction_id bigint NOT NULL,' - || ' refund_timestamp bigint NOT NULL,' - || ' coin_pub bytea NOT NULL,' - || ' reason text NOT NULL,' - || ' refund_amount merchant.taler_amount_currency NOT NULL,' - || ' UNIQUE (order_serial, coin_pub, rtransaction_id)' - || ')', s); - - -- merchant_reports - EXECUTE format('CREATE TABLE %I.merchant_reports (' - || ' report_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' report_program_section text NOT NULL,' - || ' report_description text NOT NULL,' - || ' mime_type text NOT NULL,' - || ' report_token bytea NOT NULL,' - || ' data_source text NOT NULL,' - || ' target_address text NOT NULL,' - || ' frequency bigint NOT NULL,' - || ' frequency_shift bigint NOT NULL,' - || ' next_transmission bigint NOT NULL,' - || ' last_error_code integer,' - || ' last_error_detail text,' - || ' one_shot_hidden boolean DEFAULT false,' - || ' CONSTRAINT merchant_reports_report_token_check CHECK ((length(report_token) = 32))' - || ')', s); - - -- merchant_used_tokens - EXECUTE format('CREATE TABLE %I.merchant_used_tokens (' - || ' spent_token_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' h_contract_terms bytea NOT NULL,' - || ' token_family_key_serial bigint,' - || ' token_pub bytea NOT NULL UNIQUE,' - || ' token_sig bytea NOT NULL,' - || ' blind_sig bytea NOT NULL,' - || ' CONSTRAINT merchant_spent_tokens_h_contract_terms_check CHECK ((length(h_contract_terms) = 64)),' - || ' CONSTRAINT merchant_spent_tokens_token_pub_check CHECK ((length(token_pub) = 32)),' - || ' CONSTRAINT merchant_spent_tokens_token_sig_check CHECK ((length(token_sig) = 64))' - || ')', s); - - -- merchant_statistic_amount_event - EXECUTE format('CREATE TABLE %I.merchant_statistic_amount_event (' - || ' aevent_serial_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' imeta_serial_id bigint,' - || ' slot bigint NOT NULL,' - || ' delta_curr character varying(12) NOT NULL,' - || ' delta_value bigint NOT NULL,' - || ' delta_frac integer NOT NULL,' - || ' CONSTRAINT event_key UNIQUE (imeta_serial_id, delta_curr, slot)' - || ')', s); - - -- merchant_statistic_bucket_amount - EXECUTE format('CREATE TABLE %I.merchant_statistic_bucket_amount (' - || ' bmeta_serial_id bigint NOT NULL,' - || ' bucket_start bigint NOT NULL,' - || ' bucket_range merchant.statistic_range NOT NULL,' - || ' curr character varying(12) NOT NULL,' - || ' cumulative_value bigint NOT NULL,' - || ' cumulative_frac integer NOT NULL,' - || ' PRIMARY KEY (bmeta_serial_id, curr, bucket_start, bucket_range)' - || ')', s); - - -- merchant_statistic_bucket_counter - EXECUTE format('CREATE TABLE %I.merchant_statistic_bucket_counter (' - || ' bmeta_serial_id bigint NOT NULL,' - || ' bucket_start bigint NOT NULL,' - || ' bucket_range merchant.statistic_range NOT NULL,' - || ' cumulative_number bigint NOT NULL,' - || ' PRIMARY KEY (bmeta_serial_id, bucket_start, bucket_range)' - || ')', s); - - -- merchant_statistic_bucket_meta - EXECUTE format('CREATE TABLE %I.merchant_statistic_bucket_meta (' - || ' bmeta_serial_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' slug text NOT NULL,' - || ' description text NOT NULL,' - || ' stype merchant.statistic_type NOT NULL,' - || ' ranges merchant.statistic_range[] NOT NULL,' - || ' ages integer[] NOT NULL,' - || ' CONSTRAINT equal_array_length CHECK ((array_length(ranges, 1) = array_length(ages, 1))),' - || ' UNIQUE (slug, stype)' - || ')', s); - - -- merchant_statistic_counter_event - EXECUTE format('CREATE TABLE %I.merchant_statistic_counter_event (' - || ' nevent_serial_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' imeta_serial_id bigint,' - || ' slot bigint NOT NULL,' - || ' delta bigint NOT NULL,' - || ' UNIQUE (imeta_serial_id, slot)' - || ')', s); - - -- merchant_statistic_interval_amount - EXECUTE format('CREATE TABLE %I.merchant_statistic_interval_amount (' - || ' imeta_serial_id bigint NOT NULL,' - || ' event_delimiter bigint NOT NULL,' - || ' range bigint NOT NULL,' - || ' curr character varying(12) NOT NULL,' - || ' cumulative_value bigint NOT NULL,' - || ' cumulative_frac integer NOT NULL,' - || ' PRIMARY KEY (imeta_serial_id, curr, range)' - || ')', s); - - -- merchant_statistic_interval_counter - EXECUTE format('CREATE TABLE %I.merchant_statistic_interval_counter (' - || ' imeta_serial_id bigint NOT NULL,' - || ' range bigint NOT NULL,' - || ' event_delimiter bigint NOT NULL,' - || ' cumulative_number bigint NOT NULL,' - || ' PRIMARY KEY (imeta_serial_id, range)' - || ')', s); - - -- merchant_statistic_interval_meta - EXECUTE format('CREATE TABLE %I.merchant_statistic_interval_meta (' - || ' imeta_serial_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' slug text NOT NULL,' - || ' description text NOT NULL,' - || ' stype merchant.statistic_type NOT NULL,' - || ' ranges bigint[] NOT NULL,' - || ' precisions bigint[] NOT NULL,' - || ' CONSTRAINT equal_array_length CHECK ((array_length(ranges, 1) = array_length(precisions, 1))),' - || ' CONSTRAINT merchant_statistic_interval_meta_precisions_check CHECK ((array_length(precisions, 1) > 0)),' - || ' CONSTRAINT merchant_statistic_interval_meta_ranges_check CHECK ((array_length(ranges, 1) > 0)),' - || ' UNIQUE (slug, stype)' - || ')', s); - - -- merchant_template - EXECUTE format('CREATE TABLE %I.merchant_template (' - || ' template_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' template_id text NOT NULL UNIQUE,' - || ' template_description text NOT NULL,' - || ' otp_device_id bigint,' - || ' template_contract jsonb NOT NULL,' - || ' editable_defaults jsonb' - || ')', s); - - -- merchant_token_families - EXECUTE format($DDL$CREATE TABLE %I.merchant_token_families ( - token_family_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - slug text NOT NULL UNIQUE, - name text NOT NULL, - description text, - description_i18n jsonb NOT NULL, - valid_after bigint NOT NULL, - valid_before bigint NOT NULL, - duration bigint NOT NULL, - kind text NOT NULL, - issued bigint DEFAULT 0, - used bigint DEFAULT 0, - validity_granularity bigint DEFAULT '2592000000000'::bigint NOT NULL, - start_offset bigint DEFAULT 0 NOT NULL, - cipher_choice text DEFAULT 'rsa(2048)'::text NOT NULL, - extra_data jsonb, - CONSTRAINT merchant_token_families_kind_check - CHECK ((kind = ANY (ARRAY['subscription'::text, 'discount'::text]))), - CONSTRAINT merchant_token_families_validity_granularity_check - CHECK ((validity_granularity = ANY (ARRAY[(60000000)::bigint, - '3600000000'::bigint, '86400000000'::bigint, '604800000000'::bigint, - '2592000000000'::bigint, '7776000000000'::bigint, - '31536000000000'::bigint]))) - )$DDL$, s); - - -- merchant_token_family_keys - EXECUTE format('CREATE TABLE %I.merchant_token_family_keys (' - || ' token_family_key_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' token_family_serial bigint,' - || ' pub bytea NOT NULL,' - || ' h_pub bytea NOT NULL UNIQUE,' - || ' priv bytea,' - || ' cipher text NOT NULL,' - || ' signature_validity_start bigint DEFAULT 0 NOT NULL,' - || ' signature_validity_end bigint DEFAULT 0 NOT NULL,' - || ' private_key_deleted_at bigint DEFAULT 0 NOT NULL,' - || ' private_key_created_at bigint DEFAULT 0 NOT NULL,' - || ' CONSTRAINT h_pub_length_check CHECK ((length(h_pub) = 64)),' - || $DDL$ CONSTRAINT merchant_token_family_keys_cipher_check CHECK ((cipher = ANY (ARRAY['rsa'::text, 'cs'::text])))$DDL$ - || ')', s); - - -- merchant_transfer_signatures - EXECUTE format('CREATE TABLE %I.merchant_transfer_signatures (' - || ' expected_credit_serial bigint NOT NULL PRIMARY KEY,' - || ' signkey_serial bigint NOT NULL,' - || ' wire_fee merchant.taler_amount_currency NOT NULL,' - || ' credit_amount merchant.taler_amount_currency NOT NULL,' - || ' execution_time bigint NOT NULL,' - || ' exchange_sig bytea NOT NULL,' - || ' CONSTRAINT merchant_transfer_signatures_exchange_sig_check CHECK ((length(exchange_sig) = 64))' - || ')', s); - - -- merchant_transfers - EXECUTE format('CREATE TABLE %I.merchant_transfers (' - || ' credit_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' exchange_url text NOT NULL,' - || ' wtid bytea,' - || ' credit_amount merchant.taler_amount_currency NOT NULL,' - || ' account_serial bigint NOT NULL,' - || ' bank_serial_id bigint,' - || ' expected boolean DEFAULT false,' - || ' execution_time bigint DEFAULT 0,' - || ' CONSTRAINT merchant_transfers_wtid_check CHECK ((length(wtid) = 32)),' - || ' CONSTRAINT merchant_transfers_unique UNIQUE (wtid, exchange_url, account_serial, bank_serial_id)' - || ')', s); - - -- merchant_unclaim_signatures - EXECUTE format('CREATE TABLE %I.merchant_unclaim_signatures (' - || ' unclaim_serial bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,' - || ' h_contract_terms bytea NOT NULL,' - || ' unclaim_sig bytea NOT NULL PRIMARY KEY,' - || ' expiration_time bigint NOT NULL,' - || ' CONSTRAINT merchant_unclaim_signatures_h_contract_terms_check CHECK ((length(h_contract_terms) = 64)),' - || ' CONSTRAINT merchant_unclaim_signatures_unclaim_sig_check CHECK ((length(unclaim_sig) = 64))' - || ')', s); - - -- merchant_webhook - EXECUTE format('CREATE TABLE %I.merchant_webhook (' - || ' webhook_serial bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,' - || ' webhook_id text NOT NULL UNIQUE,' - || ' event_type text NOT NULL,' - || ' url text NOT NULL,' - || ' http_method text NOT NULL,' - || ' header_template text,' - || ' body_template text' - || ')', s); - - -- tan_challenges - EXECUTE format('CREATE TABLE %I.tan_challenges (' - || ' challenge_id bigint GENERATED BY DEFAULT AS IDENTITY UNIQUE,' - || ' h_body bytea NOT NULL,' - || ' salt bytea NOT NULL,' - || ' op merchant.op_enum NOT NULL,' - || ' code text NOT NULL,' - || ' creation_date bigint NOT NULL,' - || ' expiration_date bigint NOT NULL,' - || ' retransmission_date bigint DEFAULT 0 NOT NULL,' - || ' confirmation_date bigint,' - || ' retry_counter integer NOT NULL,' - || ' tan_channel merchant.tan_enum NOT NULL,' - || ' required_address text NOT NULL,' - || ' CONSTRAINT tan_challenges_h_body_check CHECK ((length(h_body) = 32)),' - || ' CONSTRAINT tan_challenges_salt_check CHECK ((length(salt) = 16))' - || ')', s); - - -- ------------------------------------------------------------------- - -- 3. CREATE INDEX statements (per-instance only). - -- Index names are bare; PostgreSQL scopes them by schema, so the - -- same logical name in each instance schema is fine. - -- merchant_serial leading columns are dropped from the column list. - -- ------------------------------------------------------------------- - - -- merchant_product_categories - EXECUTE format('CREATE INDEX merchant_categories_by_category' - || ' ON %I.merchant_product_categories USING btree (category_serial)', s); - EXECUTE format('CREATE INDEX merchant_categories_by_product' - || ' ON %I.merchant_product_categories USING btree (product_serial)', s); - - -- merchant_contract_terms - EXECUTE format('CREATE INDEX merchant_contract_terms_by_expiration' - || ' ON %I.merchant_contract_terms USING btree (paid, pay_deadline)', s); - EXECUTE format('CREATE INDEX merchant_contract_terms_by_merchant_and_expiration' - || ' ON %I.merchant_contract_terms USING btree (pay_deadline)', s); - EXECUTE format('CREATE INDEX merchant_contract_terms_by_merchant_and_payment' - || ' ON %I.merchant_contract_terms USING btree (paid)', s); - EXECUTE format('CREATE INDEX merchant_contract_terms_by_merchant_and_session' - || ' ON %I.merchant_contract_terms USING btree (session_id)', s); - EXECUTE format('CREATE INDEX merchant_contract_terms_by_merchant_session_and_fulfillment' - || ' ON %I.merchant_contract_terms USING btree (fulfillment_url, session_id)', s); - - -- merchant_deposit_confirmations - EXECUTE format('CREATE INDEX merchant_deposit_confirmations_by_pending_wire' - || ' ON %I.merchant_deposit_confirmations USING btree (exchange_url, wire_transfer_deadline)' - || ' WHERE wire_pending', s); - - -- merchant_deposits - EXECUTE format('CREATE INDEX merchant_deposits_by_deposit_confirmation' - || ' ON %I.merchant_deposits USING btree (deposit_confirmation_serial)', s); - EXECUTE format('CREATE INDEX merchant_deposits_by_deposit_confirmation_serial' - || ' ON %I.merchant_deposits USING btree (deposit_confirmation_serial)', s); - EXECUTE format('CREATE INDEX merchant_deposits_by_settlement_open' - || ' ON %I.merchant_deposits USING btree (settlement_retry_time)' - || ' WHERE settlement_retry_needed', s); - - -- merchant_expected_transfers - EXECUTE format('CREATE INDEX merchant_expected_transfers_by_open' - || ' ON %I.merchant_expected_transfers USING btree (retry_time)' - || ' WHERE ((NOT confirmed) OR retry_needed)', s); - - -- merchant_inventory - EXECUTE format('CREATE INDEX merchant_inventory_by_image_hash' - || ' ON %I.merchant_inventory USING btree (image_hash)', s); - - -- merchant_inventory_locks - EXECUTE format('CREATE INDEX merchant_inventory_locks_by_expiration' - || ' ON %I.merchant_inventory_locks USING btree (expiration)', s); - EXECUTE format('CREATE INDEX merchant_inventory_locks_by_uuid' - || ' ON %I.merchant_inventory_locks USING btree (lock_uuid)', s); - - -- merchant_kyc - EXECUTE format('CREATE INDEX merchant_kyc_by_next_kyc_poll' - || ' ON %I.merchant_kyc USING btree (next_kyc_poll)', s); - - -- merchant_login_tokens - EXECUTE format('CREATE INDEX merchant_login_tokens_by_expiration' - || ' ON %I.merchant_login_tokens USING btree (expiration_time)', s); - - -- merchant_orders - EXECUTE format('CREATE INDEX merchant_orders_by_creation_time' - || ' ON %I.merchant_orders USING btree (creation_time)', s); - EXECUTE format('CREATE INDEX merchant_orders_by_expiration' - || ' ON %I.merchant_orders USING btree (pay_deadline)', s); - EXECUTE format('CREATE INDEX merchant_orders_by_merchant_and_fullfilment_and_session' - || ' ON %I.merchant_orders USING btree (fulfillment_url, session_id)', s); - EXECUTE format('CREATE INDEX merchant_orders_by_merchant_and_session' - || ' ON %I.merchant_orders USING btree (session_id)', s); - - -- merchant_order_locks - EXECUTE format('CREATE INDEX merchant_orders_locks_by_order_and_product' - || ' ON %I.merchant_order_locks USING btree (order_serial, product_serial)', s); - - -- merchant_refunds - EXECUTE format('CREATE INDEX merchant_refunds_by_coin_and_order' - || ' ON %I.merchant_refunds USING btree (coin_pub, order_serial)', s); - - -- merchant_expected_transfer_to_coin - EXECUTE format('CREATE INDEX merchant_transfers_by_credit' - || ' ON %I.merchant_expected_transfer_to_coin USING btree (expected_credit_serial)', s); - - -- merchant_unclaim_signatures - EXECUTE format('CREATE INDEX merchant_unclaim_signatures_by_expiration' - || ' ON %I.merchant_unclaim_signatures USING btree (expiration_time)', s); - - -- tan_challenges - EXECUTE format('CREATE INDEX tan_challenges_expiration_index' - || ' ON %I.tan_challenges USING btree (expiration_date)', s); - - -- trigram indexes - EXECUTE format('CREATE INDEX trgm_idx_categories_by_name' - || ' ON %I.merchant_categories USING gin (lower(category_name) public.gin_trgm_ops)', s); - EXECUTE format($DDL$CREATE INDEX trgm_idx_contract_summaries - ON %I.merchant_contract_terms USING gin (lower((contract_terms ->> 'summary'::text)) public.gin_trgm_ops)$DDL$, s); - EXECUTE format($DDL$CREATE INDEX trgm_idx_order_summaries - ON %I.merchant_orders USING gin (lower((contract_terms ->> 'summary'::text)) public.gin_trgm_ops)$DDL$, s); - EXECUTE format('CREATE INDEX trgm_idx_products_by_description' - || ' ON %I.merchant_inventory USING gin (lower(description) public.gin_trgm_ops)', s); - EXECUTE format('CREATE INDEX trgm_idx_products_by_name' - || ' ON %I.merchant_inventory USING gin (lower(product_name) public.gin_trgm_ops)', s); - - -- ------------------------------------------------------------------- - -- 4. ALTER TABLE for FKs *between per-instance tables* (intra-schema). - -- Both sides live in `s`; use format(..., s, s). - -- ------------------------------------------------------------------- - - -- merchant_deposit_confirmations -> merchant_accounts - EXECUTE format('ALTER TABLE %I.merchant_deposit_confirmations' - || ' ADD CONSTRAINT merchant_deposit_confirmations_account_serial_fkey' - || ' FOREIGN KEY (account_serial)' - || ' REFERENCES %I.merchant_accounts(account_serial) ON DELETE CASCADE', s, s); - - -- merchant_deposit_confirmations -> merchant_contract_terms - EXECUTE format('ALTER TABLE %I.merchant_deposit_confirmations' - || ' ADD CONSTRAINT merchant_deposit_confirmations_order_serial_fkey' - || ' FOREIGN KEY (order_serial)' - || ' REFERENCES %I.merchant_contract_terms(order_serial) ON DELETE CASCADE', s, s); - - -- merchant_deposits -> merchant_deposit_confirmations - EXECUTE format('ALTER TABLE %I.merchant_deposits' - || ' ADD CONSTRAINT merchant_deposits_deposit_confirmation_serial_fkey' - || ' FOREIGN KEY (deposit_confirmation_serial)' - || ' REFERENCES %I.merchant_deposit_confirmations(deposit_confirmation_serial) ON DELETE CASCADE', s, s); - - -- merchant_deposits -> merchant_expected_transfers (no ON DELETE clause originally) - EXECUTE format('ALTER TABLE %I.merchant_deposits' - || ' ADD CONSTRAINT merchant_deposits_settlement_expected_credit_serial_fkey' - || ' FOREIGN KEY (settlement_expected_credit_serial)' - || ' REFERENCES %I.merchant_expected_transfers(expected_credit_serial)', s, s); - - -- merchant_expected_transfer_to_coin -> merchant_deposits - EXECUTE format('ALTER TABLE %I.merchant_expected_transfer_to_coin' - || ' ADD CONSTRAINT merchant_expected_transfer_to_coin_deposit_serial_fkey' - || ' FOREIGN KEY (deposit_serial)' - || ' REFERENCES %I.merchant_deposits(deposit_serial) ON DELETE CASCADE', s, s); - - -- merchant_expected_transfer_to_coin -> merchant_expected_transfers - EXECUTE format('ALTER TABLE %I.merchant_expected_transfer_to_coin' - || ' ADD CONSTRAINT merchant_expected_transfer_to_coin_expected_credit_serial_fkey' - || ' FOREIGN KEY (expected_credit_serial)' - || ' REFERENCES %I.merchant_expected_transfers(expected_credit_serial) ON DELETE CASCADE', s, s); - - -- merchant_expected_transfers -> merchant_accounts - EXECUTE format('ALTER TABLE %I.merchant_expected_transfers' - || ' ADD CONSTRAINT merchant_expected_transfers_account_serial_fkey' - || ' FOREIGN KEY (account_serial)' - || ' REFERENCES %I.merchant_accounts(account_serial) ON DELETE CASCADE', s, s); - - -- merchant_inventory -> merchant_money_pots - EXECUTE format('ALTER TABLE %I.merchant_inventory' - || ' ADD CONSTRAINT merchant_inventory_money_pot_serial_fkey' - || ' FOREIGN KEY (money_pot_serial)' - || ' REFERENCES %I.merchant_money_pots(money_pot_serial) ON DELETE SET NULL', s, s); - - -- merchant_inventory -> merchant_product_groups - EXECUTE format('ALTER TABLE %I.merchant_inventory' - || ' ADD CONSTRAINT merchant_inventory_product_group_serial_fkey' - || ' FOREIGN KEY (product_group_serial)' - || ' REFERENCES %I.merchant_product_groups(product_group_serial) ON DELETE SET NULL', s, s); - - -- merchant_inventory_locks -> merchant_inventory - EXECUTE format('ALTER TABLE %I.merchant_inventory_locks' - || ' ADD CONSTRAINT merchant_inventory_locks_product_serial_fkey' - || ' FOREIGN KEY (product_serial)' - || ' REFERENCES %I.merchant_inventory(product_serial) ON DELETE CASCADE', s, s); - - -- merchant_issued_tokens -> merchant_token_family_keys - EXECUTE format('ALTER TABLE %I.merchant_issued_tokens' - || ' ADD CONSTRAINT merchant_issued_tokens_token_family_key_serial_fkey' - || ' FOREIGN KEY (token_family_key_serial)' - || ' REFERENCES %I.merchant_token_family_keys(token_family_key_serial) ON DELETE CASCADE', s, s); - - -- merchant_kyc -> merchant_accounts - EXECUTE format('ALTER TABLE %I.merchant_kyc' - || ' ADD CONSTRAINT merchant_kyc_account_serial_fkey' - || ' FOREIGN KEY (account_serial)' - || ' REFERENCES %I.merchant_accounts(account_serial) ON DELETE CASCADE', s, s); - - -- merchant_order_locks -> merchant_orders - EXECUTE format('ALTER TABLE %I.merchant_order_locks' - || ' ADD CONSTRAINT merchant_order_locks_order_serial_fkey' - || ' FOREIGN KEY (order_serial)' - || ' REFERENCES %I.merchant_orders(order_serial) ON DELETE CASCADE', s, s); - - -- merchant_order_locks -> merchant_inventory (no ON DELETE clause originally) - EXECUTE format('ALTER TABLE %I.merchant_order_locks' - || ' ADD CONSTRAINT merchant_order_locks_product_serial_fkey' - || ' FOREIGN KEY (product_serial)' - || ' REFERENCES %I.merchant_inventory(product_serial)', s, s); - - -- merchant_order_token_blinded_sigs -> merchant_contract_terms - EXECUTE format('ALTER TABLE %I.merchant_order_token_blinded_sigs' - || ' ADD CONSTRAINT merchant_order_token_blinded_sigs_order_serial_fkey' - || ' FOREIGN KEY (order_serial)' - || ' REFERENCES %I.merchant_contract_terms(order_serial) ON DELETE CASCADE', s, s); - - -- merchant_pending_webhooks -> merchant_webhook - EXECUTE format('ALTER TABLE %I.merchant_pending_webhooks' - || ' ADD CONSTRAINT merchant_pending_webhooks_webhook_serial_fkey' - || ' FOREIGN KEY (webhook_serial)' - || ' REFERENCES %I.merchant_webhook(webhook_serial) ON DELETE CASCADE', s, s); - - -- merchant_product_categories -> merchant_categories - EXECUTE format('ALTER TABLE %I.merchant_product_categories' - || ' ADD CONSTRAINT merchant_product_categories_category_serial_fkey' - || ' FOREIGN KEY (category_serial)' - || ' REFERENCES %I.merchant_categories(category_serial) ON DELETE CASCADE', s, s); - - -- merchant_product_categories -> merchant_inventory - EXECUTE format('ALTER TABLE %I.merchant_product_categories' - || ' ADD CONSTRAINT merchant_product_categories_product_serial_fkey' - || ' FOREIGN KEY (product_serial)' - || ' REFERENCES %I.merchant_inventory(product_serial) ON DELETE CASCADE', s, s); - - -- merchant_refund_proofs -> merchant_refunds - EXECUTE format('ALTER TABLE %I.merchant_refund_proofs' - || ' ADD CONSTRAINT merchant_refund_proofs_refund_serial_fkey' - || ' FOREIGN KEY (refund_serial)' - || ' REFERENCES %I.merchant_refunds(refund_serial) ON DELETE CASCADE', s, s); - - -- merchant_refunds -> merchant_contract_terms - EXECUTE format('ALTER TABLE %I.merchant_refunds' - || ' ADD CONSTRAINT merchant_refunds_order_serial_fkey' - || ' FOREIGN KEY (order_serial)' - || ' REFERENCES %I.merchant_contract_terms(order_serial) ON DELETE CASCADE', s, s); - - -- merchant_used_tokens -> merchant_token_family_keys - EXECUTE format('ALTER TABLE %I.merchant_used_tokens' - || ' ADD CONSTRAINT merchant_spent_tokens_token_family_key_serial_fkey' - || ' FOREIGN KEY (token_family_key_serial)' - || ' REFERENCES %I.merchant_token_family_keys(token_family_key_serial) ON DELETE CASCADE', s, s); - - -- merchant_statistic_amount_event -> merchant_statistic_interval_meta - EXECUTE format('ALTER TABLE %I.merchant_statistic_amount_event' - || ' ADD CONSTRAINT merchant_statistic_amount_event_imeta_serial_id_fkey' - || ' FOREIGN KEY (imeta_serial_id)' - || ' REFERENCES %I.merchant_statistic_interval_meta(imeta_serial_id) ON DELETE CASCADE', s, s); - - -- merchant_statistic_bucket_amount -> merchant_statistic_bucket_meta - EXECUTE format('ALTER TABLE %I.merchant_statistic_bucket_amount' - || ' ADD CONSTRAINT merchant_statistic_bucket_amount_bmeta_serial_id_fkey' - || ' FOREIGN KEY (bmeta_serial_id)' - || ' REFERENCES %I.merchant_statistic_bucket_meta(bmeta_serial_id) ON DELETE CASCADE', s, s); - - -- merchant_statistic_bucket_counter -> merchant_statistic_bucket_meta - EXECUTE format('ALTER TABLE %I.merchant_statistic_bucket_counter' - || ' ADD CONSTRAINT merchant_statistic_bucket_counter_bmeta_serial_id_fkey' - || ' FOREIGN KEY (bmeta_serial_id)' - || ' REFERENCES %I.merchant_statistic_bucket_meta(bmeta_serial_id) ON DELETE CASCADE', s, s); - - -- merchant_statistic_counter_event -> merchant_statistic_interval_meta - EXECUTE format('ALTER TABLE %I.merchant_statistic_counter_event' - || ' ADD CONSTRAINT merchant_statistic_counter_event_imeta_serial_id_fkey' - || ' FOREIGN KEY (imeta_serial_id)' - || ' REFERENCES %I.merchant_statistic_interval_meta(imeta_serial_id) ON DELETE CASCADE', s, s); - - -- merchant_statistic_interval_amount -> merchant_statistic_amount_event - EXECUTE format('ALTER TABLE %I.merchant_statistic_interval_amount' - || ' ADD CONSTRAINT merchant_statistic_interval_amount_event_delimiter_fkey' - || ' FOREIGN KEY (event_delimiter)' - || ' REFERENCES %I.merchant_statistic_amount_event(aevent_serial_id) ON DELETE RESTRICT', s, s); - - -- merchant_statistic_interval_amount -> merchant_statistic_interval_meta - EXECUTE format('ALTER TABLE %I.merchant_statistic_interval_amount' - || ' ADD CONSTRAINT merchant_statistic_interval_amount_imeta_serial_id_fkey' - || ' FOREIGN KEY (imeta_serial_id)' - || ' REFERENCES %I.merchant_statistic_interval_meta(imeta_serial_id) ON DELETE CASCADE', s, s); - - -- merchant_statistic_interval_counter -> merchant_statistic_counter_event - EXECUTE format('ALTER TABLE %I.merchant_statistic_interval_counter' - || ' ADD CONSTRAINT merchant_statistic_interval_counter_event_delimiter_fkey' - || ' FOREIGN KEY (event_delimiter)' - || ' REFERENCES %I.merchant_statistic_counter_event(nevent_serial_id) ON DELETE RESTRICT', s, s); - - -- merchant_statistic_interval_counter -> merchant_statistic_interval_meta - EXECUTE format('ALTER TABLE %I.merchant_statistic_interval_counter' - || ' ADD CONSTRAINT merchant_statistic_interval_counter_imeta_serial_id_fkey' - || ' FOREIGN KEY (imeta_serial_id)' - || ' REFERENCES %I.merchant_statistic_interval_meta(imeta_serial_id) ON DELETE CASCADE', s, s); - - -- merchant_template -> merchant_otp_devices - EXECUTE format('ALTER TABLE %I.merchant_template' - || ' ADD CONSTRAINT merchant_template_otp_device_id_fkey' - || ' FOREIGN KEY (otp_device_id)' - || ' REFERENCES %I.merchant_otp_devices(otp_serial) ON DELETE SET NULL', s, s); - - -- merchant_token_family_keys -> merchant_token_families - EXECUTE format('ALTER TABLE %I.merchant_token_family_keys' - || ' ADD CONSTRAINT merchant_token_family_keys_token_family_serial_fkey' - || ' FOREIGN KEY (token_family_serial)' - || ' REFERENCES %I.merchant_token_families(token_family_serial) ON DELETE CASCADE', s, s); - - -- merchant_transfer_signatures -> merchant_expected_transfers - EXECUTE format('ALTER TABLE %I.merchant_transfer_signatures' - || ' ADD CONSTRAINT merchant_transfer_signatures_expected_credit_serial_fkey' - || ' FOREIGN KEY (expected_credit_serial)' - || ' REFERENCES %I.merchant_expected_transfers(expected_credit_serial) ON DELETE CASCADE', s, s); - - -- merchant_transfers -> merchant_accounts - EXECUTE format('ALTER TABLE %I.merchant_transfers' - || ' ADD CONSTRAINT merchant_transfers_account_serial_fkey' - || ' FOREIGN KEY (account_serial)' - || ' REFERENCES %I.merchant_accounts(account_serial) ON DELETE CASCADE', s, s); - - -- ------------------------------------------------------------------- - -- 5. ALTER TABLE for FKs to *global* merchant.* tables. - -- Right-hand side keeps `merchant.` qualification. - -- ------------------------------------------------------------------- - - -- merchant_builtin_unit_overrides -> merchant.merchant_builtin_units - EXECUTE format('ALTER TABLE %I.merchant_builtin_unit_overrides' - || ' ADD CONSTRAINT merchant_builtin_unit_overrides_builtin_unit_serial_fkey' - || ' FOREIGN KEY (builtin_unit_serial)' - || ' REFERENCES merchant.merchant_builtin_units(unit_serial) ON DELETE CASCADE', s); - - -- merchant_deposit_confirmations -> merchant.merchant_exchange_signing_keys - EXECUTE format('ALTER TABLE %I.merchant_deposit_confirmations' - || ' ADD CONSTRAINT merchant_deposit_confirmations_signkey_serial_fkey' - || ' FOREIGN KEY (signkey_serial)' - || ' REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE', s); - - -- merchant_deposits -> merchant.merchant_exchange_signing_keys - EXECUTE format('ALTER TABLE %I.merchant_deposits' - || ' ADD CONSTRAINT merchant_deposits_signkey_serial_fkey' - || ' FOREIGN KEY (signkey_serial)' - || ' REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE', s); - - -- merchant_donau_instances -> merchant.merchant_donau_keys (FK on donau_url) - EXECUTE format('ALTER TABLE %I.merchant_donau_instances' - || ' ADD CONSTRAINT merchant_donau_instances_donau_url_fkey' - || ' FOREIGN KEY (donau_url)' - || ' REFERENCES merchant.merchant_donau_keys(donau_url) ON DELETE CASCADE', s); - - -- merchant_expected_transfers -> merchant.merchant_exchange_signing_keys - EXECUTE format('ALTER TABLE %I.merchant_expected_transfers' - || ' ADD CONSTRAINT merchant_expected_transfers_signkey_serial_fkey' - || ' FOREIGN KEY (signkey_serial)' - || ' REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE', s); - - -- merchant_refund_proofs -> merchant.merchant_exchange_signing_keys - EXECUTE format('ALTER TABLE %I.merchant_refund_proofs' - || ' ADD CONSTRAINT merchant_refund_proofs_signkey_serial_fkey' - || ' FOREIGN KEY (signkey_serial)' - || ' REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE', s); - - -- merchant_transfer_signatures -> merchant.merchant_exchange_signing_keys - EXECUTE format('ALTER TABLE %I.merchant_transfer_signatures' - || ' ADD CONSTRAINT merchant_transfer_signatures_signkey_serial_fkey' - || ' FOREIGN KEY (signkey_serial)' - || ' REFERENCES merchant.merchant_exchange_signing_keys(signkey_serial) ON DELETE CASCADE', s); - - -- ===================================================================== - -- (FKs back to merchant.merchant_instances are intentionally dropped: - -- the existence of the per-instance schema implies that relationship.) - -- ===================================================================== diff --git a/src/backenddb/pg_create_instance_schema_triggers.sql.fragment b/src/backenddb/pg_create_instance_schema_triggers.sql.fragment @@ -1,736 +0,0 @@ --- ===================================================================== --- Per-instance schema constructor: triggers and trigger functions. --- --- Embedded inside merchant.create_instance_schema(BIGINT); local var --- `s TEXT` holds the per-instance schema name. All DDL is via --- EXECUTE format(...) with %I quoting. --- --- The merchant_serial column has been dropped from every per-instance --- table; references to it inside the original trigger bodies are --- removed here. Helper functions (replace_placeholder, ...) and --- bump_*_stat procedures live in `merchant.` and stay qualified. --- ===================================================================== - - -- ------------------------------------------------------------------- - -- 1. CREATE FUNCTION for each per-instance trigger (17 functions). - -- ------------------------------------------------------------------- - - -- handle_category_changes - EXECUTE format($OUTER$ - CREATE FUNCTION %I.handle_category_changes() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - DECLARE - resolved_body TEXT; - webhook RECORD; - BEGIN - IF TG_OP = 'INSERT' THEN - FOR webhook IN - SELECT webhook_serial, - url, - http_method, - body_template - FROM merchant_webhook - WHERE event_type = 'category_added' - LOOP - resolved_body := webhook.body_template; - resolved_body := merchant.replace_placeholder(resolved_body, - 'webhook_type', - 'category_added'); - resolved_body := merchant.replace_placeholder(resolved_body, - 'category_serial', - NEW.category_serial::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'category_name', - NEW.category_name); - INSERT INTO merchant_pending_webhooks - (webhook_serial, url, http_method, body) - VALUES - (webhook.webhook_serial, - webhook.url, - webhook.http_method, - resolved_body); - END LOOP; - NOTIFY XXJWF6C1DCS1255RJH7GQ1EK16J8DMRSQ6K9EDKNKCP7HRVWAJPKG; - END IF; - IF TG_OP = 'UPDATE' THEN - FOR webhook IN - SELECT webhook_serial, - url, - http_method, - body_template - FROM merchant_webhook - WHERE event_type = 'category_updated' - LOOP - resolved_body := webhook.body_template; - resolved_body := merchant.replace_placeholder(resolved_body, - 'webhook_type', - 'category_updated'); - resolved_body := merchant.replace_placeholder(resolved_body, - 'category_serial', - NEW.category_serial::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_category_name', - OLD.category_name); - resolved_body := merchant.replace_placeholder(resolved_body, - 'category_name', - NEW.category_name); - resolved_body := merchant.replace_placeholder(resolved_body, - 'category_name_i18n', - NEW.category_name_i18n::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_category_name_i18n', - OLD.category_name_i18n::TEXT); - INSERT INTO merchant_pending_webhooks - (webhook_serial, url, http_method, body) - VALUES - (webhook.webhook_serial, - webhook.url, - webhook.http_method, - resolved_body); - END LOOP; - NOTIFY XXJWF6C1DCS1255RJH7GQ1EK16J8DMRSQ6K9EDKNKCP7HRVWAJPKG; - END IF; - IF TG_OP = 'DELETE' THEN - FOR webhook IN - SELECT webhook_serial, - url, - http_method, - body_template - FROM merchant_webhook - WHERE event_type = 'category_deleted' - LOOP - resolved_body := webhook.body_template; - resolved_body := merchant.replace_placeholder(resolved_body, - 'webhook_type', - 'category_deleted'); - resolved_body := merchant.replace_placeholder(resolved_body, - 'category_serial', - OLD.category_serial::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'category_name', - OLD.category_name); - INSERT INTO merchant_pending_webhooks - (webhook_serial, url, http_method, body) - VALUES - (webhook.webhook_serial, - webhook.url, - webhook.http_method, - resolved_body); - END LOOP; - NOTIFY XXJWF6C1DCS1255RJH7GQ1EK16J8DMRSQ6K9EDKNKCP7HRVWAJPKG; - END IF; - RETURN NULL; - END; - $BODY$ - $OUTER$, s); - - -- handle_inventory_changes - EXECUTE format($OUTER$ - CREATE FUNCTION %I.handle_inventory_changes() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - DECLARE - resolved_body TEXT; - webhook RECORD; - BEGIN - IF TG_OP = 'INSERT' THEN - FOR webhook IN - SELECT webhook_serial, - url, - http_method, - body_template - FROM merchant_webhook - WHERE event_type = 'inventory_added' - LOOP - resolved_body := webhook.body_template; - resolved_body := merchant.replace_placeholder(resolved_body, - 'webhook_type', - 'inventory_added'); - resolved_body := merchant.replace_placeholder(resolved_body, - 'product_serial', - NEW.product_serial::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'product_id', - NEW.product_id); - resolved_body := merchant.replace_placeholder(resolved_body, - 'description', - NEW.description); - resolved_body := merchant.replace_placeholder(resolved_body, - 'description_i18n', - NEW.description_i18n::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'unit', - NEW.unit); - resolved_body := merchant.replace_placeholder(resolved_body, - 'image', - NEW.image); - resolved_body := merchant.replace_placeholder(resolved_body, - 'taxes', - NEW.taxes::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'price', - NEW.price_array[1]::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'unit_price', - NEW.price_array::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_stock', - NEW.total_stock::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_sold', - NEW.total_sold::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_lost', - NEW.total_lost::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'address', - NEW.address::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'next_restock', - NEW.next_restock::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'minimum_age', - NEW.minimum_age::TEXT); - INSERT INTO merchant_pending_webhooks - (webhook_serial, url, http_method, body) - VALUES - (webhook.webhook_serial, - webhook.url, - webhook.http_method, - resolved_body); - END LOOP; - NOTIFY XXJWF6C1DCS1255RJH7GQ1EK16J8DMRSQ6K9EDKNKCP7HRVWAJPKG; - END IF; - IF TG_OP = 'UPDATE' THEN - FOR webhook IN - SELECT webhook_serial, - url, - http_method, - body_template - FROM merchant_webhook - WHERE event_type = 'inventory_updated' - LOOP - resolved_body := webhook.body_template; - resolved_body := merchant.replace_placeholder(resolved_body, - 'webhook_type', - 'inventory_updated'); - resolved_body := merchant.replace_placeholder(resolved_body, - 'product_serial', - NEW.product_serial::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'product_id', - NEW.product_id); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_description', - OLD.description); - resolved_body := merchant.replace_placeholder(resolved_body, - 'description', - NEW.description); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_description_i18n', - OLD.description_i18n::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'description_i18n', - NEW.description_i18n::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_unit', - OLD.unit); - resolved_body := merchant.replace_placeholder(resolved_body, - 'unit', - NEW.unit); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_image', - OLD.image); - resolved_body := merchant.replace_placeholder(resolved_body, - 'image', - NEW.image); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_taxes', - OLD.taxes::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'taxes', - NEW.taxes::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_price', - OLD.price_array[1]::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_unit_price', - OLD.price_array::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'price', - NEW.price_array[1]::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'unit_price', - NEW.price_array::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_total_stock', - OLD.total_stock::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_stock', - NEW.total_stock::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_total_sold', - OLD.total_sold::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_sold', - NEW.total_sold::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_total_lost', - OLD.total_lost::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_lost', - NEW.total_lost::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_address', - OLD.address::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'address', - NEW.address::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_next_restock', - OLD.next_restock::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'next_restock', - NEW.next_restock::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'old_minimum_age', - OLD.minimum_age::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'minimum_age', - NEW.minimum_age::TEXT); - INSERT INTO merchant_pending_webhooks - (webhook_serial, url, http_method, body) - VALUES - (webhook.webhook_serial, - webhook.url, - webhook.http_method, - resolved_body); - END LOOP; - NOTIFY XXJWF6C1DCS1255RJH7GQ1EK16J8DMRSQ6K9EDKNKCP7HRVWAJPKG; - END IF; - IF TG_OP = 'DELETE' THEN - FOR webhook IN - SELECT webhook_serial, - url, - http_method, - body_template - FROM merchant_webhook - WHERE event_type = 'inventory_deleted' - LOOP - resolved_body := webhook.body_template; - resolved_body := merchant.replace_placeholder(resolved_body, - 'webhook_type', - 'inventory_deleted'); - resolved_body := merchant.replace_placeholder(resolved_body, - 'product_serial', - OLD.product_serial::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'product_id', - OLD.product_id); - resolved_body := merchant.replace_placeholder(resolved_body, - 'description', - OLD.description); - resolved_body := merchant.replace_placeholder(resolved_body, - 'description_i18n', - OLD.description_i18n::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'unit', - OLD.unit); - resolved_body := merchant.replace_placeholder(resolved_body, - 'image', - OLD.image); - resolved_body := merchant.replace_placeholder(resolved_body, - 'taxes', - OLD.taxes::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'price', - OLD.price_array[1]::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'unit_price', - OLD.price_array::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_stock', - OLD.total_stock::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_sold', - OLD.total_sold::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'total_lost', - OLD.total_lost::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'address', - OLD.address::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'next_restock', - OLD.next_restock::TEXT); - resolved_body := merchant.replace_placeholder(resolved_body, - 'minimum_age', - OLD.minimum_age::TEXT); - INSERT INTO merchant_pending_webhooks - (webhook_serial, url, http_method, body) - VALUES - (webhook.webhook_serial, - webhook.url, - webhook.http_method, - resolved_body); - END LOOP; - NOTIFY XXJWF6C1DCS1255RJH7GQ1EK16J8DMRSQ6K9EDKNKCP7HRVWAJPKG; - END IF; - RETURN NULL; - END; - $BODY$ - $OUTER$, s); - - -- merchant_contract_terms_insert_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_contract_terms_insert_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_number_stat - ('orders-claimed' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,1); - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_contract_terms_update_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_contract_terms_update_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - DECLARE - my_rec RECORD; - BEGIN - IF (NEW.wired AND NOT OLD.wired) - THEN - CALL merchant_do_bump_number_stat - ('orders-settled' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,1); - END IF; - IF (NEW.paid AND NOT OLD.paid) - THEN - CALL merchant_do_bump_number_stat - ('orders-paid' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,1); - FOR my_rec IN - SELECT total_without_fee - FROM merchant_deposit_confirmations - WHERE order_serial = NEW.order_serial - LOOP - CALL merchant_do_bump_amount_stat - ('payments-received-after-deposit-fee' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,my_rec.total_without_fee); - END LOOP; - FOR my_rec IN - SELECT deposit_fee - FROM merchant_deposits - WHERE deposit_confirmation_serial IN - (SELECT deposit_confirmation_serial - FROM merchant_deposit_confirmations - WHERE order_serial = NEW.order_serial) - LOOP - CALL merchant_do_bump_amount_stat - ('total-deposit-fees-paid' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,my_rec.deposit_fee); - END LOOP; - END IF; - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_deposits_insert_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_deposits_insert_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_amount_stat - ('deposits-received' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,NEW.amount_with_fee); - CALL merchant_do_bump_amount_stat - ('deposits-fees-paid' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,NEW.deposit_fee); - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_expected_transfers_insert_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_expected_transfers_insert_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - IF NEW.wire_fee IS NOT NULL - THEN - CALL merchant_do_bump_amount_stat - ('wire-fees-paid' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,NEW.wire_fee); - END IF; - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_expected_transfers_insert_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_expected_transfers_insert_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - UPDATE merchant_transfers - SET expected = TRUE - WHERE wtid = NEW.wtid - AND exchange_url = NEW.exchange_url - AND credit_amount = NEW.expected_credit_amount; - NEW.confirmed = FOUND; - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_expected_transfers_update_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_expected_transfers_update_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - IF NEW.wire_fee IS NOT NULL AND OLD.wire_fee IS NULL - THEN - CALL merchant_do_bump_amount_stat - ('wire-fees-paid' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,NEW.wire_fee); - END IF; - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_expected_transfers_update_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_expected_transfers_update_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - UPDATE merchant_transfers - SET expected = TRUE - WHERE wtid = NEW.wtid - AND exchange_url = NEW.exchange_url - AND credit_amount = NEW.expected_credit_amount - AND NOT expected; - NEW.confirmed = NEW.confirmed OR FOUND; - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_issued_tokens_insert_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_issued_tokens_insert_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_number_stat - ('tokens-issued' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,1); - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_kyc_insert_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_kyc_insert_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant.merchant_send_kyc_notification(NEW.account_serial, - NEW.exchange_url); - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_kyc_update_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_kyc_update_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - IF (OLD.kyc_ok - ,OLD.last_rule_gen - ,OLD.aml_review - ,OLD.jaccount_limits) - IS DISTINCT FROM - (NEW.kyc_ok - ,NEW.last_rule_gen - ,NEW.aml_review - ,NEW.jaccount_limits) - THEN - CALL merchant.merchant_send_kyc_notification(NEW.account_serial, - NEW.exchange_url); - END IF; - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_orders_insert_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_orders_insert_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_number_stat - ('orders-created' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,1); - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_refunds_insert_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_refunds_insert_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_amount_stat - ('refunds-granted' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,NEW.refund_amount); - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_transfer_signatures_insert_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_transfer_signatures_insert_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_amount_stat - ('wire-fees-paid' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,NEW.wire_fee); - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_transfers_insert_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_transfers_insert_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - UPDATE merchant_expected_transfers - SET confirmed = TRUE - WHERE wtid = NEW.wtid - AND exchange_url = NEW.exchange_url - AND expected_credit_amount = NEW.credit_amount; - NEW.expected = FOUND; - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- merchant_used_tokens_insert_statistics_trigger - EXECUTE format($OUTER$ - CREATE FUNCTION %I.merchant_used_tokens_insert_statistics_trigger() RETURNS trigger - LANGUAGE plpgsql - AS $BODY$ - BEGIN - CALL merchant_do_bump_number_stat - ('tokens-used' - ,CURRENT_TIMESTAMP(0)::TIMESTAMP - ,1); - RETURN NEW; - END $BODY$ - $OUTER$, s); - - -- ------------------------------------------------------------------- - -- 2. CREATE TRIGGER attachments (16 triggers). - -- ------------------------------------------------------------------- - - EXECUTE format('CREATE TRIGGER merchant_contract_terms_on_insert_statistic' - || ' AFTER INSERT ON %I.merchant_contract_terms' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_contract_terms_insert_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_contract_terms_on_update_statistic' - || ' AFTER UPDATE ON %I.merchant_contract_terms' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_contract_terms_update_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_deposits_on_insert_statistic' - || ' AFTER INSERT ON %I.merchant_deposits' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_deposits_insert_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_expected_transfers_on_insert' - || ' BEFORE INSERT ON %I.merchant_expected_transfers' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_expected_transfers_insert_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_expected_transfers_on_insert_statistic' - || ' AFTER INSERT ON %I.merchant_expected_transfers' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_expected_transfers_insert_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_expected_transfers_on_update' - || ' BEFORE UPDATE ON %I.merchant_expected_transfers' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_expected_transfers_update_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_expected_transfers_on_update_statistic' - || ' AFTER INSERT ON %I.merchant_expected_transfers' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_expected_transfers_update_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_issued_tokens_on_insert_statistic' - || ' AFTER INSERT ON %I.merchant_issued_tokens' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_issued_tokens_insert_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_kyc_on_insert' - || ' AFTER INSERT ON %I.merchant_kyc' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_kyc_insert_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_kyc_on_update' - || ' AFTER UPDATE ON %I.merchant_kyc' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_kyc_update_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_orders_on_insert_statistic' - || ' AFTER INSERT ON %I.merchant_orders' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_orders_insert_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_refunds_on_insert_statistic' - || ' AFTER INSERT ON %I.merchant_refunds' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_refunds_insert_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_transfers_on_insert' - || ' BEFORE INSERT ON %I.merchant_transfers' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_transfers_insert_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER merchant_used_tokens_on_insert_statistic' - || ' AFTER INSERT ON %I.merchant_used_tokens' - || ' FOR EACH ROW EXECUTE FUNCTION %I.merchant_used_tokens_insert_statistics_trigger()', - s, s); - - EXECUTE format('CREATE TRIGGER trigger_category_changes' - || ' AFTER INSERT OR DELETE OR UPDATE ON %I.merchant_categories' - || ' FOR EACH ROW EXECUTE FUNCTION %I.handle_category_changes()', - s, s); - - EXECUTE format('CREATE TRIGGER trigger_inventory_changes' - || ' AFTER INSERT OR DELETE OR UPDATE ON %I.merchant_inventory' - || ' FOR EACH ROW EXECUTE FUNCTION %I.handle_inventory_changes()', - s, s); diff --git a/src/backenddb/pg_create_instance_trigger.sql b/src/backenddb/pg_create_instance_trigger.sql @@ -1,57 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.merchant_instances_after_insert_trigger() CASCADE; -CREATE FUNCTION merchant.merchant_instances_after_insert_trigger() - RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - PERFORM merchant.create_instance_schema(NEW.merchant_serial); - RETURN NULL; -END $$; -COMMENT ON FUNCTION merchant.merchant_instances_after_insert_trigger() - IS 'Builds the per-instance schema merchant_instance_<merchant_serial>' - ' for the newly inserted row.'; - -DROP TRIGGER IF EXISTS merchant_instances_on_insert - ON merchant.merchant_instances; -CREATE TRIGGER merchant_instances_on_insert - AFTER INSERT ON merchant.merchant_instances - FOR EACH ROW - EXECUTE FUNCTION merchant.merchant_instances_after_insert_trigger(); - - -DROP FUNCTION IF EXISTS merchant.merchant_instances_after_delete_trigger() CASCADE; -CREATE FUNCTION merchant.merchant_instances_after_delete_trigger() - RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - EXECUTE format('DROP SCHEMA IF EXISTS %I CASCADE', - 'merchant_instance_' || OLD.merchant_serial::TEXT); - RETURN NULL; -END $$; -COMMENT ON FUNCTION merchant.merchant_instances_after_delete_trigger() - IS 'Drops the per-instance schema merchant_instance_<merchant_serial>' - ' when its row in merchant.merchant_instances is removed.'; - -DROP TRIGGER IF EXISTS merchant_instances_on_delete - ON merchant.merchant_instances; -CREATE TRIGGER merchant_instances_on_delete - AFTER DELETE ON merchant.merchant_instances - FOR EACH ROW - EXECUTE FUNCTION merchant.merchant_instances_after_delete_trigger(); diff --git a/src/backenddb/pg_expire_locks.sql b/src/backenddb/pg_expire_locks.sql @@ -1,54 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.expire_locks(INT8); -CREATE FUNCTION merchant.expire_locks(IN p_now INT8) -RETURNS INT8 -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - total INT8 := 0; - affected INT8; -BEGIN - FOR rec IN - SELECT merchant_serial FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - EXECUTE format('DELETE FROM %I.merchant_inventory_locks' - ' WHERE expiration < $1', s) USING p_now; - GET DIAGNOSTICS affected = ROW_COUNT; - total := total + affected; - - EXECUTE format('DELETE FROM %I.merchant_orders' - ' WHERE pay_deadline < $1', s) USING p_now; - GET DIAGNOSTICS affected = ROW_COUNT; - total := total + affected; - - EXECUTE format('DELETE FROM %I.merchant_contract_terms' - ' WHERE NOT paid' - ' AND pay_deadline < $1', s) USING p_now; - GET DIAGNOSTICS affected = ROW_COUNT; - total := total + affected; - END LOOP; - RETURN total; -END -$FN$; -COMMENT ON FUNCTION merchant.expire_locks(INT8) - IS 'Loops over all instance schemas and DELETEs expired inventory locks,' - ' unpaid orders past their pay_deadline, and unpaid contracts past their' - ' pay_deadline. Returns the total number of rows deleted.'; diff --git a/src/backenddb/pg_lookup_instances.sql b/src/backenddb/pg_lookup_instances.sql @@ -1,95 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.lookup_instances(BOOLEAN, TEXT); -CREATE FUNCTION merchant.lookup_instances( - IN p_active_only BOOLEAN, - IN p_merchant_id TEXT) -RETURNS TABLE( - out_merchant_serial BIGINT, - out_merchant_pub BYTEA, - out_auth_hash BYTEA, - out_auth_salt BYTEA, - out_merchant_priv BYTEA, - out_merchant_id TEXT, - out_merchant_name TEXT, - out_address JSONB, - out_jurisdiction JSONB, - out_use_stefan BOOLEAN, - out_phone_validated BOOLEAN, - out_email_validated BOOLEAN, - out_default_wire_transfer_delay INT8, - out_default_pay_delay INT8, - out_default_refund_delay INT8, - out_website TEXT, - out_email TEXT, - out_phone_number TEXT, - out_logo BYTEA, - out_default_wire_transfer_rounding_interval time_rounder_interval) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - pkey BYTEA; -BEGIN - FOR rec IN - SELECT mi.* - FROM merchant.merchant_instances mi - WHERE p_merchant_id IS NULL - OR mi.merchant_id = p_merchant_id - LOOP - pkey := NULL; - BEGIN - EXECUTE format('SELECT merchant_priv FROM %I.merchant_keys', - 'merchant_instance_' || rec.merchant_serial::TEXT) - INTO pkey; - EXCEPTION - WHEN undefined_table THEN - pkey := NULL; - END; - IF p_active_only AND pkey IS NULL THEN - CONTINUE; - END IF; - out_merchant_serial := rec.merchant_serial; - out_merchant_pub := rec.merchant_pub; - out_auth_hash := rec.auth_hash; - out_auth_salt := rec.auth_salt; - out_merchant_priv := pkey; - out_merchant_id := rec.merchant_id; - out_merchant_name := rec.merchant_name; - out_address := rec.address; - out_jurisdiction := rec.jurisdiction; - out_use_stefan := rec.use_stefan; - out_phone_validated := rec.phone_validated; - out_email_validated := rec.email_validated; - out_default_wire_transfer_delay := rec.default_wire_transfer_delay; - out_default_pay_delay := rec.default_pay_delay; - out_default_refund_delay := rec.default_refund_delay; - out_website := rec.website; - out_email := rec.email; - out_phone_number := rec.phone_number; - out_logo := rec.logo; - out_default_wire_transfer_rounding_interval := - rec.default_wire_transfer_rounding_interval; - RETURN NEXT; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.lookup_instances(BOOLEAN, TEXT) - IS 'Returns one row per matching instance, joining merchant.merchant_instances' - ' with the per-instance merchant_keys table (merchant_priv NULL if absent).' - ' If p_active_only is true, instances without a private key are skipped.' - ' If p_merchant_id is non-NULL, only the matching instance is returned.'; diff --git a/src/backenddb/pg_lookup_pending_deposits.sql b/src/backenddb/pg_lookup_pending_deposits.sql @@ -1,230 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.lookup_pending_deposits(TEXT, INT8, INT8, BOOLEAN); -CREATE FUNCTION merchant.lookup_pending_deposits( - IN p_exchange_url TEXT, - IN p_now INT8, - IN p_limit INT8, - IN p_allow_future BOOLEAN) -RETURNS TABLE( - out_deposit_serial INT8, - out_h_contract_terms BYTEA, - out_merchant_priv BYTEA, - out_merchant_id TEXT, - out_wire_transfer_deadline INT8, - out_retry_time INT8, - out_h_wire BYTEA, - out_amount_with_fee taler_amount_currency, - out_deposit_fee taler_amount_currency, - out_coin_pub BYTEA) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - pkey BYTEA; - inner_rec RECORD; - remaining INT8 := p_limit; -BEGIN - FOR rec IN - SELECT merchant_serial, merchant_id FROM merchant.merchant_instances - LOOP - EXIT WHEN remaining <= 0; - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - pkey := NULL; - BEGIN - EXECUTE format('SELECT merchant_priv FROM %I.merchant_keys', s) - INTO pkey; - EXCEPTION - WHEN undefined_table THEN - pkey := NULL; - END; - BEGIN - FOR inner_rec IN - EXECUTE format( - 'SELECT' - ' md.deposit_serial AS deposit_serial' - ' ,mct.h_contract_terms AS h_contract_terms' - ' ,mdc.wire_transfer_deadline AS wire_transfer_deadline' - ' ,md.settlement_retry_time AS retry_time' - ' ,ma.h_wire AS h_wire' - ' ,md.amount_with_fee AS amount_with_fee' - ' ,md.deposit_fee AS deposit_fee' - ' ,md.coin_pub AS coin_pub' - ' FROM %I.merchant_deposits md' - ' JOIN %I.merchant_deposit_confirmations mdc' - ' USING (deposit_confirmation_serial)' - ' JOIN %I.merchant_contract_terms mct' - ' ON (mct.order_serial=mdc.order_serial)' - ' JOIN %I.merchant_accounts ma' - ' ON (mdc.account_serial=ma.account_serial)' - ' LEFT JOIN %I.merchant_kyc kyc' - ' ON (mdc.account_serial=kyc.account_serial)' - ' WHERE (mdc.exchange_url=$1)' - ' AND md.settlement_retry_needed' - ' AND ($4 OR (md.settlement_retry_time < $2))' - ' AND ( (kyc.kyc_ok IS NULL) OR kyc.kyc_ok)' - ' ORDER BY md.settlement_retry_time ASC' - ' LIMIT $3', - s, s, s, s, s) - USING p_exchange_url, p_now, remaining, p_allow_future - LOOP - out_deposit_serial := inner_rec.deposit_serial; - out_h_contract_terms := inner_rec.h_contract_terms; - out_merchant_priv := pkey; - out_merchant_id := rec.merchant_id; - out_wire_transfer_deadline := inner_rec.wire_transfer_deadline; - out_retry_time := inner_rec.retry_time; - out_h_wire := inner_rec.h_wire; - out_amount_with_fee := inner_rec.amount_with_fee; - out_deposit_fee := inner_rec.deposit_fee; - out_coin_pub := inner_rec.coin_pub; - remaining := remaining - 1; - RETURN NEXT; - EXIT WHEN remaining <= 0; - END LOOP; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.lookup_pending_deposits(TEXT, INT8, INT8, BOOLEAN) - IS 'Returns up to p_limit pending-settlement deposit rows for the given exchange' - ' across all instance schemas, joined with the per-instance merchant_keys.'; - - -DROP FUNCTION IF EXISTS merchant.lookup_reports_pending(); -CREATE FUNCTION merchant.lookup_reports_pending() -RETURNS TABLE( - out_merchant_id TEXT, - out_report_serial INT8, - out_report_program_section TEXT, - out_report_description TEXT, - out_mime_type TEXT, - out_report_token BYTEA, - out_target_address TEXT, - out_frequency INT8, - out_frequency_shift INT8, - out_next_transmission INT8, - out_one_shot_hidden BOOLEAN) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - inner_rec RECORD; - found BOOLEAN := FALSE; -BEGIN - FOR rec IN - SELECT merchant_serial, merchant_id FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - EXECUTE format( - 'SELECT' - ' report_serial AS rs' - ' ,report_program_section AS rps' - ' ,report_description AS rd' - ' ,mime_type AS mt' - ' ,report_token AS rt' - ' ,target_address AS ta' - ' ,frequency AS f' - ' ,frequency_shift AS fs' - ' ,next_transmission AS nt' - ' ,one_shot_hidden AS osh' - ' FROM %I.merchant_reports' - ' ORDER BY next_transmission ASC LIMIT 1', s) - INTO inner_rec; - IF inner_rec IS NULL THEN - CONTINUE; - END IF; - IF (NOT found) OR (inner_rec.nt < out_next_transmission) THEN - out_merchant_id := rec.merchant_id; - out_report_serial := inner_rec.rs; - out_report_program_section := inner_rec.rps; - out_report_description := inner_rec.rd; - out_mime_type := inner_rec.mt; - out_report_token := inner_rec.rt; - out_target_address := inner_rec.ta; - out_frequency := inner_rec.f; - out_frequency_shift := inner_rec.fs; - out_next_transmission := inner_rec.nt; - out_one_shot_hidden := inner_rec.osh; - found := TRUE; - END IF; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; - IF found THEN - RETURN NEXT; - END IF; -END -$FN$; -COMMENT ON FUNCTION merchant.lookup_reports_pending() - IS 'Returns the single next-due report (smallest next_transmission)' - ' across all instance schemas, or no row if no reports exist.'; - - -DROP FUNCTION IF EXISTS merchant.check_report(INT8, BYTEA, TEXT); -CREATE FUNCTION merchant.check_report( - IN p_report_id INT8, - IN p_report_token BYTEA, - IN p_mime_type TEXT) -RETURNS TABLE( - out_merchant_id TEXT, - out_data_source TEXT) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - inner_rec RECORD; -BEGIN - FOR rec IN - SELECT merchant_serial, merchant_id FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - EXECUTE format( - 'SELECT data_source AS ds' - ' FROM %I.merchant_reports' - ' WHERE report_serial=$1' - ' AND report_token=$2' - ' AND mime_type=$3', s) - USING p_report_id, p_report_token, p_mime_type - INTO inner_rec; - IF inner_rec IS NOT NULL THEN - out_merchant_id := rec.merchant_id; - out_data_source := inner_rec.ds; - RETURN NEXT; - RETURN; - END IF; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.check_report(INT8, BYTEA, TEXT) - IS 'Searches all instance schemas for a report matching the given' - ' (report_serial, report_token, mime_type). Returns at most one row' - ' (the first matching instance encountered).'; diff --git a/src/backenddb/pg_lookup_pending_webhooks.sql b/src/backenddb/pg_lookup_pending_webhooks.sql @@ -1,133 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.lookup_pending_webhooks(INT8); -CREATE FUNCTION merchant.lookup_pending_webhooks(IN p_now INT8) -RETURNS TABLE( - out_webhook_pending_serial INT8, - out_next_attempt INT8, - out_retries INT4, - out_url TEXT, - out_http_method TEXT, - out_header TEXT, - out_body TEXT) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - inner_rec RECORD; -BEGIN - FOR rec IN - SELECT merchant_serial FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - FOR inner_rec IN - EXECUTE format('SELECT' - ' webhook_pending_serial AS wps' - ',next_attempt AS na' - ',retries AS r' - ',url AS u' - ',http_method AS hm' - ',header AS h' - ',body AS b' - ' FROM %I.merchant_pending_webhooks' - ' WHERE next_attempt <= $1' - ' ORDER BY next_attempt ASC', s) - USING p_now - LOOP - out_webhook_pending_serial := inner_rec.wps; - out_next_attempt := inner_rec.na; - out_retries := inner_rec.r; - out_url := inner_rec.u; - out_http_method := inner_rec.hm; - out_header := inner_rec.h; - out_body := inner_rec.b; - RETURN NEXT; - END LOOP; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.lookup_pending_webhooks(INT8) - IS 'Returns one row per pending webhook (next_attempt <= p_now) across all' - ' instance schemas, ordered per-instance by next_attempt ASC.'; - - -DROP FUNCTION IF EXISTS merchant.lookup_future_webhook(); -CREATE FUNCTION merchant.lookup_future_webhook() -RETURNS TABLE( - out_webhook_pending_serial INT8, - out_next_attempt INT8, - out_retries INT4, - out_url TEXT, - out_http_method TEXT, - out_header TEXT, - out_body TEXT) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - best RECORD; - found BOOLEAN := FALSE; -BEGIN - best := NULL; - FOR rec IN - SELECT merchant_serial FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - EXECUTE format('SELECT' - ' webhook_pending_serial AS wps' - ',next_attempt AS na' - ',retries AS r' - ',url AS u' - ',http_method AS hm' - ',header AS h' - ',body AS b' - ' FROM %I.merchant_pending_webhooks' - ' ORDER BY next_attempt ASC LIMIT 1', s) - INTO best; - IF best IS NOT NULL - AND ((NOT found) - OR (best.na < out_next_attempt)) THEN - out_webhook_pending_serial := best.wps; - out_next_attempt := best.na; - out_retries := best.r; - out_url := best.u; - out_http_method := best.hm; - out_header := best.h; - out_body := best.b; - found := TRUE; - END IF; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; - IF found THEN - RETURN NEXT; - END IF; -END -$FN$; -COMMENT ON FUNCTION merchant.lookup_future_webhook() - IS 'Returns the single soonest-due pending webhook across all instances,' - ' or no row if no instance has any pending webhook.'; diff --git a/src/backenddb/pg_select_accounts.sql b/src/backenddb/pg_select_accounts.sql @@ -1,84 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.select_accounts(TEXT); -CREATE FUNCTION merchant.select_accounts( - IN p_merchant_id TEXT) -RETURNS TABLE( - out_merchant_id TEXT, - out_merchant_priv BYTEA, - out_h_wire BYTEA, - out_salt BYTEA, - out_payto_uri TEXT, - out_credit_facade_url TEXT, - out_credit_facade_credentials JSONB, - out_extra_wire_subject_metadata TEXT, - out_active BOOLEAN) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - pkey BYTEA; -BEGIN - FOR rec IN - SELECT mi.merchant_serial, mi.merchant_id - FROM merchant.merchant_instances mi - WHERE p_merchant_id IS NULL - OR mi.merchant_id = p_merchant_id - LOOP - pkey := NULL; - BEGIN - EXECUTE format('SELECT merchant_priv FROM %I.merchant_keys', - 'merchant_instance_' || rec.merchant_serial::TEXT) - INTO pkey; - EXCEPTION - WHEN undefined_table THEN - pkey := NULL; - END; - BEGIN - FOR out_h_wire, - out_salt, - out_payto_uri, - out_credit_facade_url, - out_credit_facade_credentials, - out_extra_wire_subject_metadata, - out_active - IN EXECUTE format('SELECT' - ' h_wire' - ',salt' - ',payto_uri' - ',credit_facade_url' - ',credit_facade_credentials' - ',extra_wire_subject_metadata' - ',active' - ' FROM %I.merchant_accounts', - 'merchant_instance_' || rec.merchant_serial::TEXT) - LOOP - out_merchant_id := rec.merchant_id; - out_merchant_priv := pkey; - RETURN NEXT; - END LOOP; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.select_accounts(TEXT) - IS 'Returns one row per merchant_account across all (or one) instance schemas,' - ' joined with the per-instance merchant_keys.merchant_priv (NULL if absent).' - ' If p_merchant_id is non-NULL, only that instance is scanned.'; diff --git a/src/backenddb/pg_select_all_donau_instances.sql b/src/backenddb/pg_select_all_donau_instances.sql @@ -1,116 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.select_all_donau_instances(); -CREATE FUNCTION merchant.select_all_donau_instances() -RETURNS TABLE( - out_donau_instances_serial INT8, - out_donau_url TEXT, - out_charity_name TEXT, - out_charity_pub_key BYTEA, - out_charity_id INT8, - out_charity_max_per_year taler_amount_currency, - out_charity_receipts_to_date taler_amount_currency, - out_current_year INT8, - out_keys_json JSONB) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - inner_rec RECORD; -BEGIN - FOR rec IN - SELECT merchant_serial, merchant_pub FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - FOR inner_rec IN - EXECUTE format( - 'SELECT' - ' di.donau_instances_serial AS dis' - ' ,di.donau_url AS du' - ' ,di.charity_name AS cn' - ' ,di.charity_id AS ci' - ' ,di.charity_max_per_year AS cmp' - ' ,di.charity_receipts_to_date AS crt' - ' ,di.current_year AS cy' - ' ,dk.keys_json AS kj' - ' FROM %I.merchant_donau_instances di' - ' LEFT JOIN merchant.merchant_donau_keys dk' - ' ON di.donau_url = dk.donau_url', s) - LOOP - out_donau_instances_serial := inner_rec.dis; - out_donau_url := inner_rec.du; - out_charity_name := inner_rec.cn; - out_charity_pub_key := rec.merchant_pub; - out_charity_id := inner_rec.ci; - out_charity_max_per_year := inner_rec.cmp; - out_charity_receipts_to_date := inner_rec.crt; - out_current_year := inner_rec.cy; - out_keys_json := inner_rec.kj; - RETURN NEXT; - END LOOP; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.select_all_donau_instances() - IS 'Returns all donau-instance configurations across every per-instance' - ' schema, joined with merchant.merchant_donau_keys for the keys json' - ' and with merchant.merchant_instances for the charity public key.'; - - -DROP FUNCTION IF EXISTS merchant.select_donau_instances_filtered(TEXT); -CREATE FUNCTION merchant.select_donau_instances_filtered(IN p_currency TEXT) -RETURNS TABLE( - out_donau_url TEXT) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - inner_rec RECORD; -BEGIN - FOR rec IN - SELECT merchant_serial FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - FOR inner_rec IN - EXECUTE format( - 'SELECT donau_url AS du' - ' FROM %I.merchant_donau_instances' - ' WHERE (charity_max_per_year).curr = $1', s) - USING p_currency - LOOP - out_donau_url := inner_rec.du; - RETURN NEXT; - END LOOP; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.select_donau_instances_filtered(TEXT) - IS 'Returns the donau_url of every donau-instance configuration whose' - ' charity_max_per_year currency matches p_currency, scanning across' - ' all per-instance schemas.'; diff --git a/src/backenddb/pg_select_open_transfers.sql b/src/backenddb/pg_select_open_transfers.sql @@ -1,124 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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/> --- - -DROP FUNCTION IF EXISTS merchant.select_open_transfers(INT8); -CREATE FUNCTION merchant.select_open_transfers(IN p_limit INT8) -RETURNS TABLE( - out_expected_credit_serial INT8, - out_instance_id TEXT, - out_exchange_url TEXT, - out_payto_uri TEXT, - out_wtid BYTEA, - out_retry_time INT8) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - inner_rec RECORD; - remaining INT8 := p_limit; -BEGIN - FOR rec IN - SELECT merchant_serial, merchant_id FROM merchant.merchant_instances - LOOP - EXIT WHEN remaining <= 0; - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - FOR inner_rec IN - EXECUTE format( - 'SELECT' - ' met.expected_credit_serial AS ecs' - ' ,met.exchange_url AS exu' - ' ,ma.payto_uri AS pu' - ' ,met.wtid AS wt' - ' ,met.retry_time AS rt' - ' FROM %I.merchant_expected_transfers met' - ' JOIN %I.merchant_accounts ma USING (account_serial)' - ' WHERE retry_needed' - ' ORDER BY retry_time ASC' - ' LIMIT $1', s, s) - USING remaining - LOOP - out_expected_credit_serial := inner_rec.ecs; - out_instance_id := rec.merchant_id; - out_exchange_url := inner_rec.exu; - out_payto_uri := inner_rec.pu; - out_wtid := inner_rec.wt; - out_retry_time := inner_rec.rt; - remaining := remaining - 1; - RETURN NEXT; - EXIT WHEN remaining <= 0; - END LOOP; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.select_open_transfers(INT8) - IS 'Returns up to p_limit retry-needed expected_transfer rows across all' - ' instance schemas, joined with the per-instance merchant_accounts.'; - - -DROP FUNCTION IF EXISTS merchant.select_wirewatch_accounts(); -CREATE FUNCTION merchant.select_wirewatch_accounts() -RETURNS TABLE( - out_merchant_id TEXT, - out_payto_uri TEXT, - out_credit_facade_url TEXT, - out_credit_facade_credentials JSONB, - out_last_bank_serial INT8) -LANGUAGE plpgsql -AS $FN$ -DECLARE - rec RECORD; - s TEXT; - inner_rec RECORD; -BEGIN - FOR rec IN - SELECT merchant_serial, merchant_id FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - FOR inner_rec IN - EXECUTE format( - 'SELECT' - ' payto_uri AS pu' - ' ,credit_facade_url AS cfu' - ' ,credit_facade_credentials AS cfc' - ' ,last_bank_serial AS lbs' - ' FROM %I.merchant_accounts' - ' WHERE active' - ' AND credit_facade_url IS NOT NULL', s) - LOOP - out_merchant_id := rec.merchant_id; - out_payto_uri := inner_rec.pu; - out_credit_facade_url := inner_rec.cfu; - out_credit_facade_credentials := inner_rec.cfc; - out_last_bank_serial := inner_rec.lbs; - RETURN NEXT; - END LOOP; - EXCEPTION - WHEN undefined_table THEN - NULL; - END; - END LOOP; -END -$FN$; -COMMENT ON FUNCTION merchant.select_wirewatch_accounts() - IS 'Returns one row per active credit-facade-enabled merchant_account' - ' across all instance schemas.'; diff --git a/src/backenddb/purge_instance.c b/src/backenddb/purge_instance.c @@ -41,25 +41,8 @@ TALER_MERCHANTDB_purge_instance (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_string (merchant_id), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("SET search_path TO merchant"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; check_connection (pg); - /* The AFTER DELETE trigger will DROP the per-instance schema. Reset - search_path off it first so subsequent queries on this connection - don't reference the about-to-be-dropped schema. */ - if (GNUNET_OK != - GNUNET_PQ_exec_statements (pg->conn, - es)) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - GNUNET_free (pg->current_merchant_id); - pg->current_merchant_id = NULL; - pg->current_merchant_serial = 0; PREPARE (pg, "purge_instance", "DELETE FROM merchant_instances" diff --git a/src/backenddb/refund_coin.c b/src/backenddb/refund_coin.c @@ -34,44 +34,43 @@ TALER_MERCHANTDB_refund_coin (struct TALER_MERCHANTDB_PostgresContext *pg, const char *reason) { 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_timestamp (&refund_timestamp), GNUNET_PQ_query_param_auto_from_type (coin_pub), GNUNET_PQ_query_param_string (reason), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); - PREPARE_INSTANCE (pg, - stmt, - "refund_coin", - "INSERT INTO merchant_refunds" - "(order_serial" - ",rtransaction_id" - ",refund_timestamp" - ",coin_pub" - ",reason" - ",refund_amount" - ") " - "SELECT " - " dcon.order_serial" - ",0" /* rtransaction_id always 0 for /abort */ - ",$2" - ",dep.coin_pub" - ",$4" - ",dep.amount_with_fee" - " FROM merchant_deposits dep" - " JOIN merchant_deposit_confirmations dcon" - " USING (deposit_confirmation_serial)" - " WHERE dep.coin_pub=$3" - " AND dcon.order_serial=" - " (SELECT order_serial" - " FROM merchant_contract_terms" - " WHERE h_contract_terms=$1)"); + PREPARE (pg, + "refund_coin", + "INSERT INTO merchant_refunds" + "(order_serial" + ",rtransaction_id" + ",refund_timestamp" + ",coin_pub" + ",reason" + ",refund_amount" + ") " + "SELECT " + " dcon.order_serial" + ",0" /* rtransaction_id always 0 for /abort */ + ",$3" + ",dep.coin_pub" + ",$5" + ",dep.amount_with_fee" + " FROM merchant_deposits dep" + " JOIN merchant_deposit_confirmations dcon" + " USING (deposit_confirmation_serial)" + " WHERE dep.coin_pub=$4" + " AND dcon.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))"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "refund_coin", params); } diff --git a/src/backenddb/select_account.c b/src/backenddb/select_account.c @@ -33,6 +33,7 @@ TALER_MERCHANTDB_select_account (struct TALER_MERCHANTDB_PostgresContext *pg, struct TALER_MERCHANTDB_AccountDetails *ad) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_auto_from_type (h_wire), GNUNET_PQ_query_param_end }; @@ -57,28 +58,27 @@ TALER_MERCHANTDB_select_account (struct TALER_MERCHANTDB_PostgresContext *pg, NULL), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); ad->h_wire = *h_wire; ad->instance_id = id; check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_account", - "SELECT" - " salt" - ",payto_uri" - ",credit_facade_url" - ",credit_facade_credentials::TEXT" - ",active" - ",extra_wire_subject_metadata" - " FROM merchant_accounts" - " WHERE h_wire=$1;"); + PREPARE (pg, + "select_account", + "SELECT" + " salt" + ",payto_uri" + ",credit_facade_url" + ",credit_facade_credentials::TEXT" + ",active" + ",extra_wire_subject_metadata" + " FROM merchant_accounts" + " WHERE merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1) " + " AND (h_wire=$2);"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "select_account", params, rs); } diff --git a/src/backenddb/select_account_by_uri.c b/src/backenddb/select_account_by_uri.c @@ -33,6 +33,7 @@ TALER_MERCHANTDB_select_account_by_uri (struct TALER_MERCHANTDB_PostgresContext struct TALER_MERCHANTDB_AccountDetails *ad) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_string (payto_uri.full_payto), GNUNET_PQ_query_param_end }; @@ -53,30 +54,29 @@ TALER_MERCHANTDB_select_account_by_uri (struct TALER_MERCHANTDB_PostgresContext &ad->active), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); ad->credit_facade_url = NULL; ad->credit_facade_credentials = NULL; ad->payto_uri.full_payto = GNUNET_strdup (payto_uri.full_payto); ad->instance_id = id; check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_account_by_uri", - "SELECT" - " salt" - ",h_wire" - ",credit_facade_url" - ",credit_facade_credentials::TEXT" - ",active" - " FROM merchant_accounts" - " WHERE payto_uri = $1"); + PREPARE (pg, + "select_account_by_uri", + "SELECT" + " salt" + ",h_wire" + ",credit_facade_url" + ",credit_facade_credentials::TEXT" + ",active" + " FROM merchant_accounts" + " WHERE merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND payto_uri = $2"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "select_account_by_uri", params, rs); } diff --git a/src/backenddb/select_accounts.c b/src/backenddb/select_accounts.c @@ -80,30 +80,30 @@ select_account_cb (void *cls, struct TALER_MerchantPrivateKeyP merchant_priv; bool no_priv; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("out_h_wire", + GNUNET_PQ_result_spec_auto_from_type ("h_wire", &acc.h_wire), - GNUNET_PQ_result_spec_auto_from_type ("out_salt", + GNUNET_PQ_result_spec_auto_from_type ("salt", &acc.salt), - GNUNET_PQ_result_spec_string ("out_payto_uri", + GNUNET_PQ_result_spec_string ("payto_uri", &payto.full_payto), - GNUNET_PQ_result_spec_string ("out_merchant_id", + GNUNET_PQ_result_spec_string ("merchant_id", &instance_id), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("out_credit_facade_url", + GNUNET_PQ_result_spec_string ("credit_facade_url", &facade_url), NULL), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("out_extra_wire_subject_metadata", + GNUNET_PQ_result_spec_string ("extra_wire_subject_metadata", &extra_wire_subject_metadata), NULL), GNUNET_PQ_result_spec_allow_null ( - TALER_PQ_result_spec_json ("out_credit_facade_credentials", + TALER_PQ_result_spec_json ("credit_facade_credentials", &credential), NULL), - GNUNET_PQ_result_spec_bool ("out_active", + GNUNET_PQ_result_spec_bool ("active", &acc.active), GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_auto_from_type ("out_merchant_priv", + GNUNET_PQ_result_spec_auto_from_type ("merchant_priv", &merchant_priv), &no_priv), GNUNET_PQ_result_spec_end @@ -156,16 +156,26 @@ TALER_MERCHANTDB_select_accounts ( PREPARE (pg, "select_accounts", "SELECT" - " out_merchant_id" - " ,out_merchant_priv" - " ,out_h_wire" - " ,out_salt" - " ,out_payto_uri" - " ,out_credit_facade_url" - " ,out_credit_facade_credentials::TEXT" - " ,out_extra_wire_subject_metadata" - " ,out_active" - " FROM merchant.select_accounts($1)"); + " ma.h_wire" + ",ma.salt" + ",ma.payto_uri" + ",ma.credit_facade_url" + ",ma.credit_facade_credentials::TEXT" + ",ma.extra_wire_subject_metadata" + ",ma.active" + ",mk.merchant_priv" + ",mi.merchant_id" + " FROM merchant_accounts ma" + " JOIN merchant_instances mi" + " ON (mi.merchant_serial=ma.merchant_serial)" + " LEFT JOIN merchant_keys mk" + " ON (mk.merchant_serial=ma.merchant_serial)" + " WHERE" + " ($1::TEXT IS NULL) OR" + " (ma.merchant_serial=" + " (SELECT merchant_serial " + " FROM merchant_instances" + " WHERE merchant_id=$1));"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "select_accounts", params, diff --git a/src/backenddb/select_all_donau_instances.c b/src/backenddb/select_all_donau_instances.c @@ -76,24 +76,24 @@ select_donau_instance_cb (void *cls, int64_t current_year; json_t *donau_keys_json = NULL; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("out_donau_instances_serial", + GNUNET_PQ_result_spec_uint64 ("donau_instances_serial", &donau_instance_serial), - GNUNET_PQ_result_spec_string ("out_donau_url", + GNUNET_PQ_result_spec_string ("donau_url", &donau_url), - GNUNET_PQ_result_spec_string ("out_charity_name", + GNUNET_PQ_result_spec_string ("charity_name", &charity_name), - GNUNET_PQ_result_spec_auto_from_type ("out_charity_pub_key", + GNUNET_PQ_result_spec_auto_from_type ("charity_pub_key", &charity_pub_key), - GNUNET_PQ_result_spec_uint64 ("out_charity_id", + GNUNET_PQ_result_spec_uint64 ("charity_id", &charity_id), - TALER_PQ_result_spec_amount_with_currency ("out_charity_max_per_year", + TALER_PQ_result_spec_amount_with_currency ("charity_max_per_year", &charity_max_per_year), - TALER_PQ_result_spec_amount_with_currency ("out_charity_receipts_to_date", + TALER_PQ_result_spec_amount_with_currency ("charity_receipts_to_date", &charity_receipts_to_date), - GNUNET_PQ_result_spec_int64 ("out_current_year", + GNUNET_PQ_result_spec_int64 ("current_year", &current_year), GNUNET_PQ_result_spec_allow_null ( - TALER_PQ_result_spec_json ("out_keys_json", + TALER_PQ_result_spec_json ("keys_json", &donau_keys_json), NULL), GNUNET_PQ_result_spec_end @@ -144,16 +144,20 @@ TALER_MERCHANTDB_select_all_donau_instances ( PREPARE (pg, "select_all_donau_instances", "SELECT" - " out_donau_instances_serial" - " ,out_donau_url" - " ,out_charity_name" - " ,out_charity_pub_key" - " ,out_charity_id" - " ,out_charity_max_per_year" - " ,out_charity_receipts_to_date" - " ,out_current_year" - " ,out_keys_json::TEXT" - " FROM merchant.select_all_donau_instances()"); + " di.donau_instances_serial" + ",di.donau_url" + ",di.charity_name" + ",mi.merchant_pub AS charity_pub_key" + ",di.charity_id" + ",di.charity_max_per_year" + ",di.charity_receipts_to_date" + ",di.current_year" + ",dk.keys_json::TEXT" + " FROM merchant_donau_instances di" + " LEFT JOIN merchant_donau_keys dk" + " ON di.donau_url = dk.donau_url" + " JOIN merchant_instances mi" + " ON di.merchant_instance_serial = mi.merchant_serial"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "select_all_donau_instances", params, diff --git a/src/backenddb/select_category.c b/src/backenddb/select_category.c @@ -35,43 +35,21 @@ TALER_MERCHANTDB_select_category (struct TALER_MERCHANTDB_PostgresContext *pg, char **products) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&category_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); - check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_category", - "SELECT" - " category_name" - ",category_name_i18n::TEXT" - ",t.product_array AS products" - " FROM merchant_categories mc" - ",LATERAL (" - " SELECT ARRAY (" - " SELECT " - " mi.product_id AS product_id" - " FROM merchant_product_categories mpc" - " JOIN merchant_inventory mi" - " USING (product_serial)" - " WHERE mpc.category_serial = mc.category_serial" - " ) AS product_array" - " ) t" - " WHERE mc.category_serial=$1"); if (NULL == cd) { struct GNUNET_PQ_ResultSpec rs_null[] = { GNUNET_PQ_result_spec_end }; + check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "select_category", params, rs_null); } @@ -89,9 +67,31 @@ TALER_MERCHANTDB_select_category (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; + PREPARE (pg, + "select_category", + "SELECT" + " category_name" + ",category_name_i18n::TEXT" + ",t.product_array AS products" + " FROM merchant_categories mc" + " JOIN merchant_instances inst" + " USING (merchant_serial)" + ",LATERAL (" + " SELECT ARRAY (" + " SELECT " + " mi.product_id AS product_id" + " FROM merchant_product_categories mpc" + " JOIN merchant_inventory mi" + " USING (product_serial)" + " WHERE mpc.category_serial = mc.category_serial" + " ) AS product_array" + " ) t" + " WHERE inst.merchant_id=$1" + " AND mc.category_serial=$2"); + check_connection (pg); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "select_category", params, rs); } diff --git a/src/backenddb/select_category_by_name.c b/src/backenddb/select_category_by_name.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_select_category_by_name (struct TALER_MERCHANTDB_PostgresContex uint64_t *category_id) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (category_name), GNUNET_PQ_query_param_end }; @@ -44,23 +45,21 @@ TALER_MERCHANTDB_select_category_by_name (struct TALER_MERCHANTDB_PostgresContex name_i18n), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_category_by_name", - "SELECT" - " category_serial" - ",category_name_i18n::TEXT" - " FROM merchant_categories" - " WHERE category_name=$1"); + PREPARE (pg, + "select_category_by_name", + "SELECT" + " category_serial" + ",category_name_i18n::TEXT" + " FROM merchant_categories mc" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE mi.merchant_id=$1" + " AND mc.category_name=$2"); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "select_category_by_name", params, rs); } diff --git a/src/backenddb/select_donau_instance_by_serial.c b/src/backenddb/select_donau_instance_by_serial.c @@ -45,21 +45,17 @@ TALER_MERCHANTDB_select_donau_instance_by_serial (struct TALER_MERCHANTDB_Postgr GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_donau_instance_by_serial", - "SELECT donau_url" - " ,charity_id" - " FROM merchant_donau_instances" - " WHERE donau_instances_serial = $1"); + PREPARE (pg, + "select_donau_instance_by_serial", + "SELECT donau_url" + " ,charity_id" + " FROM merchant_donau_instances" + " WHERE donau_instances_serial = $1"); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "select_donau_instance_by_serial", params, rs); } diff --git a/src/backenddb/select_donau_instances.c b/src/backenddb/select_donau_instances.c @@ -137,36 +137,32 @@ TALER_MERCHANTDB_select_donau_instances ( .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&pg->current_merchant_serial), + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_donau_instances", - "SELECT" - " di.donau_instances_serial" - ",di.donau_url" - ",di.charity_name" - ",mi.merchant_pub AS charity_pub_key" - ",di.charity_id" - ",di.charity_max_per_year" - ",di.charity_receipts_to_date" - ",di.current_year" - ",dk.keys_json::TEXT" - " FROM merchant_donau_instances di" - " LEFT JOIN merchant_donau_keys dk" - " ON di.donau_url = dk.donau_url" - " JOIN merchant.merchant_instances mi" - " ON mi.merchant_serial = $1"); + PREPARE (pg, + "select_donau_instances", + "SELECT" + " di.donau_instances_serial" + ",di.donau_url" + ",di.charity_name" + ",mi.merchant_pub AS charity_pub_key" + ",di.charity_id" + ",di.charity_max_per_year" + ",di.charity_receipts_to_date" + ",di.current_year" + ",dk.keys_json::TEXT" + " FROM merchant_donau_instances di" + " LEFT JOIN merchant_donau_keys dk" + " ON di.donau_url = dk.donau_url" + " JOIN merchant_instances mi" + " ON di.merchant_instance_serial = mi.merchant_serial" + " WHERE mi.merchant_id = $1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - stmt, + "select_donau_instances", params, &select_donau_instance_cb, &sdc); diff --git a/src/backenddb/select_donau_instances_filtered.c b/src/backenddb/select_donau_instances_filtered.c @@ -67,7 +67,7 @@ select_donau_instance_cb (void *cls, { char *donau_url; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("out_donau_url", + GNUNET_PQ_result_spec_string ("donau_url", &donau_url), GNUNET_PQ_result_spec_end }; @@ -112,8 +112,9 @@ TALER_MERCHANTDB_select_donau_instances_filtered ( PREPARE (pg, "select_donau_instances_filtered", "SELECT" - " out_donau_url" - " FROM merchant.select_donau_instances_filtered($1)"); + " donau_url" + " FROM merchant_donau_instances" + " WHERE (charity_max_per_year).curr = $1"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "select_donau_instances_filtered", diff --git a/src/backenddb/select_login_token.c b/src/backenddb/select_login_token.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_select_login_token (struct TALER_MERCHANTDB_PostgresContext *pg uint32_t *validity_scope) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_auto_from_type (token), GNUNET_PQ_query_param_end }; @@ -44,22 +45,21 @@ TALER_MERCHANTDB_select_login_token (struct TALER_MERCHANTDB_PostgresContext *pg validity_scope), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_login_token", - "SELECT" - " expiration_time" - ",validity_scope" - " FROM merchant_login_tokens" - " WHERE token=$1"); + PREPARE (pg, + "select_login_token", + "SELECT" + " expiration_time" + ",validity_scope" + " FROM merchant_login_tokens" + " WHERE token=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "select_login_token", params, rs); } diff --git a/src/backenddb/select_money_pot.c b/src/backenddb/select_money_pot.c @@ -36,6 +36,7 @@ TALER_MERCHANTDB_select_money_pot (struct TALER_MERCHANTDB_PostgresContext *pg, struct TALER_Amount **pot_totals) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&money_pot_id), GNUNET_PQ_query_param_end }; @@ -50,24 +51,22 @@ TALER_MERCHANTDB_select_money_pot (struct TALER_MERCHANTDB_PostgresContext *pg, pot_totals), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_money_pot", - "SELECT" - " money_pot_name" - " ,money_pot_description" - " ,pot_totals" - " FROM merchant_money_pots" - " WHERE money_pot_serial=$1;"); + PREPARE (pg, + "select_money_pot", + "SELECT" + " mp.money_pot_name" + " ,mp.money_pot_description" + " ,mp.pot_totals" + " FROM merchant_money_pots mp" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE merchant_id=$1" + " AND money_pot_serial=$2;"); return GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "select_money_pot", params, rs); } diff --git a/src/backenddb/select_money_pots.c b/src/backenddb/select_money_pots.c @@ -122,45 +122,45 @@ TALER_MERCHANTDB_select_money_pots (struct TALER_MERCHANTDB_PostgresContext *pg, .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&offset), GNUNET_PQ_query_param_uint64 (&plimit), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_asc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_asc, - "lookup_money_pots_asc", - "SELECT" - " money_pot_serial" - " ,money_pot_name" - " ,pot_totals" - " FROM merchant_money_pots" - " WHERE money_pot_serial > $1" - " ORDER BY money_pot_serial ASC" - " LIMIT $2"); - PREPARE_INSTANCE (pg, - stmt_desc, - "lookup_money_pots_desc", - "SELECT" - " money_pot_serial" - " ,money_pot_name" - " ,pot_totals" - " FROM merchant_money_pots" - " WHERE money_pot_serial < $1" - " ORDER BY money_pot_serial DESC" - " LIMIT $2"); + PREPARE (pg, + "lookup_money_pots_asc", + "SELECT" + " money_pot_serial" + " ,money_pot_name" + " ,pot_totals" + " FROM merchant_money_pots" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND money_pot_serial > $2" + " ORDER BY money_pot_serial ASC" + " LIMIT $3"); + PREPARE (pg, + "lookup_money_pots_desc", + "SELECT" + " money_pot_serial" + " ,money_pot_name" + " ,pot_totals" + " FROM merchant_money_pots" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND money_pot_serial < $2" + " ORDER BY money_pot_serial DESC" + " LIMIT $3"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, (limit > 0) - ? stmt_asc - : stmt_desc, + ? "lookup_money_pots_asc" + : "lookup_money_pots_desc", params, &lookup_money_pots_cb, &plc); diff --git a/src/backenddb/select_open_transfers.c b/src/backenddb/select_open_transfers.c @@ -77,17 +77,17 @@ open_transfers_cb (void *cls, struct TALER_WireTransferIdentifierRawP wtid; struct GNUNET_TIME_Absolute retry_time; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("out_expected_credit_serial", + GNUNET_PQ_result_spec_uint64 ("expected_credit_serial", &rowid), - GNUNET_PQ_result_spec_string ("out_instance_id", + GNUNET_PQ_result_spec_string ("instance_id", &instance_id), - GNUNET_PQ_result_spec_string ("out_exchange_url", + GNUNET_PQ_result_spec_string ("exchange_url", &exchange_url), - GNUNET_PQ_result_spec_string ("out_payto_uri", + GNUNET_PQ_result_spec_string ("payto_uri", &payto_uri.full_payto), - GNUNET_PQ_result_spec_auto_from_type ("out_wtid", + GNUNET_PQ_result_spec_auto_from_type ("wtid", &wtid), - GNUNET_PQ_result_spec_absolute_time ("out_retry_time", + GNUNET_PQ_result_spec_absolute_time ("retry_time", &retry_time), GNUNET_PQ_result_spec_end }; @@ -134,13 +134,20 @@ TALER_MERCHANTDB_select_open_transfers ( PREPARE (pg, "select_open_transfers", "SELECT" - " out_expected_credit_serial" - " ,out_instance_id" - " ,out_exchange_url" - " ,out_payto_uri" - " ,out_wtid" - " ,out_retry_time" - " FROM merchant.select_open_transfers($1)"); + " met.expected_credit_serial" + ",mi.merchant_id AS instance_id" + ",met.exchange_url" + ",ma.payto_uri" + ",met.wtid" + ",met.retry_time" + " FROM merchant_expected_transfers met" + " JOIN merchant_accounts ma" + " USING (account_serial)" + " JOIN merchant_instances mi" + " ON (ma.merchant_serial=mi.merchant_serial)" + " WHERE retry_needed" + " ORDER BY retry_time ASC" + " LIMIT $1;"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, "select_open_transfers", diff --git a/src/backenddb/select_order_blinded_sigs.c b/src/backenddb/select_order_blinded_sigs.c @@ -106,23 +106,19 @@ TALER_MERCHANTDB_select_order_blinded_sigs (struct TALER_MERCHANTDB_PostgresCont GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_blinded_sigs", - "SELECT" - " motbs.token_blinded_signature" - " ,motbs.token_hash" - " FROM merchant_order_token_blinded_sigs AS motbs" - " JOIN merchant_contract_terms AS mct USING (order_serial)" - " WHERE mct.order_id = $1" - " ORDER BY motbs.token_index ASC"); + PREPARE (pg, + "select_blinded_sigs", + "SELECT" + " motbs.token_blinded_signature" + " ,motbs.token_hash" + " FROM merchant_order_token_blinded_sigs AS motbs" + " JOIN merchant_contract_terms AS mct USING (order_serial)" + " WHERE mct.order_id = $1" + " ORDER BY motbs.token_index ASC"); return GNUNET_PQ_eval_prepared_multi_select ( pg->conn, - stmt, + "select_blinded_sigs", params, &restore_sig_cb, &ctx); diff --git a/src/backenddb/select_otp.c b/src/backenddb/select_otp.c @@ -33,33 +33,32 @@ TALER_MERCHANTDB_select_otp (struct TALER_MERCHANTDB_PostgresContext *pg, struct TALER_MERCHANTDB_OtpDeviceDetails *td) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (otp_id), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); - check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_otp", - "SELECT" - " otp_description" - ",otp_ctr" - ",otp_key" - ",otp_algorithm" - " FROM merchant_otp_devices" - " WHERE otp_id=$1"); + PREPARE (pg, + "select_otp", + "SELECT" + " otp_description" + ",otp_ctr" + ",otp_key" + ",otp_algorithm" + " FROM merchant_otp_devices" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND merchant_otp_devices.otp_id=$2"); 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, - stmt, + "select_otp", params, rs_null); } @@ -79,8 +78,9 @@ TALER_MERCHANTDB_select_otp (struct TALER_MERCHANTDB_PostgresContext *pg, }; enum GNUNET_DB_QueryStatus qs; + check_connection (pg); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "select_otp", params, rs); td->otp_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos32; diff --git a/src/backenddb/select_otp_serial.c b/src/backenddb/select_otp_serial.c @@ -33,6 +33,7 @@ TALER_MERCHANTDB_select_otp_serial (struct TALER_MERCHANTDB_PostgresContext *pg, uint64_t *serial) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (otp_id), GNUNET_PQ_query_param_end }; @@ -41,21 +42,19 @@ TALER_MERCHANTDB_select_otp_serial (struct TALER_MERCHANTDB_PostgresContext *pg, serial), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_otp_serial", - "SELECT" - " otp_serial" - " FROM merchant_otp_devices" - " WHERE otp_id=$1"); + PREPARE (pg, + "select_otp_serial", + "SELECT" + " otp_serial" + " FROM merchant_otp_devices" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND merchant_otp_devices.otp_id=$2"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "select_otp_serial", params, rs); } diff --git a/src/backenddb/select_product_groups.c b/src/backenddb/select_product_groups.c @@ -112,45 +112,45 @@ TALER_MERCHANTDB_select_product_groups (struct TALER_MERCHANTDB_PostgresContext .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&offset), GNUNET_PQ_query_param_uint64 (&plimit), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_asc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_asc, - "lookup_product_groups_asc", - "SELECT" - " product_group_serial" - " ,product_group_name" - " ,product_group_description" - " FROM merchant_product_groups" - " WHERE product_group_serial > $1" - " ORDER BY product_group_serial ASC" - " LIMIT $2"); - PREPARE_INSTANCE (pg, - stmt_desc, - "lookup_product_groups_desc", - "SELECT" - " product_group_serial" - " ,product_group_name" - " ,product_group_description" - " FROM merchant_product_groups" - " WHERE product_group_serial < $1" - " ORDER BY product_group_serial DESC" - " LIMIT $2"); + PREPARE (pg, + "lookup_product_groups_asc", + "SELECT" + " product_group_serial" + " ,product_group_name" + " ,product_group_description" + " FROM merchant_product_groups" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND product_group_serial > $2" + " ORDER BY product_group_serial ASC" + " LIMIT $3"); + PREPARE (pg, + "lookup_product_groups_desc", + "SELECT" + " product_group_serial" + " ,product_group_name" + " ,product_group_description" + " FROM merchant_product_groups" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND product_group_serial < $2" + " ORDER BY product_group_serial DESC" + " LIMIT $3"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, (limit > 0) - ? stmt_asc - : stmt_desc, + ? "lookup_product_groups_asc" + : "lookup_product_groups_desc", params, &lookup_product_groups_cb, &plc); diff --git a/src/backenddb/select_report.c b/src/backenddb/select_report.c @@ -41,6 +41,7 @@ TALER_MERCHANTDB_select_report (struct TALER_MERCHANTDB_PostgresContext *pg, char **last_error_detail) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&report_id), GNUNET_PQ_query_param_end }; @@ -73,33 +74,31 @@ TALER_MERCHANTDB_select_report (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); *last_error_detail = NULL; code = TALER_EC_NONE; check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "select_report", - "SELECT" - " report_program_section" - " ,report_description" - " ,mime_type" - " ,data_source" - " ,target_address" - " ,frequency" - " ,frequency_shift" - " ,next_transmission" - " ,last_error_code" - " ,last_error_detail" - " FROM merchant_reports" - " WHERE report_serial=$1;"); + PREPARE (pg, + "select_report", + "SELECT" + " mr.report_program_section" + " ,mr.report_description" + " ,mr.mime_type" + " ,mr.data_source" + " ,mr.target_address" + " ,mr.frequency" + " ,mr.frequency_shift" + " ,mr.next_transmission" + " ,mr.last_error_code" + " ,mr.last_error_detail" + " FROM merchant_reports mr" + " JOIN merchant_instances mi" + " USING (merchant_serial)" + " WHERE merchant_id=$1" + " AND report_serial=$2;"); qs = GNUNET_PQ_eval_prepared_singleton_select ( pg->conn, - stmt, + "select_report", params, rs); *last_error_code = (enum TALER_ErrorCode) code; diff --git a/src/backenddb/select_reports.c b/src/backenddb/select_reports.c @@ -113,47 +113,47 @@ TALER_MERCHANTDB_select_reports ( .extract_failed = false, }; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&offset), GNUNET_PQ_query_param_uint64 (&plimit), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt_asc[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_desc[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_asc, - "select_reports_asc", - "SELECT" - " report_serial" - " ,report_description" - " ,frequency" - " FROM merchant_reports" - " WHERE report_serial > $1" - " AND NOT one_shot_hidden" - " ORDER BY report_serial ASC" - " LIMIT $2"); - PREPARE_INSTANCE (pg, - stmt_desc, - "select_reports_desc", - "SELECT" - " report_serial" - " ,report_description" - " ,frequency" - " FROM merchant_reports" - " WHERE report_serial < $1" - " AND NOT one_shot_hidden" - " ORDER BY report_serial DESC" - " LIMIT $2"); + PREPARE (pg, + "select_reports_asc", + "SELECT" + " report_serial" + " ,report_description" + " ,frequency" + " FROM merchant_reports" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND report_serial > $2" + " AND NOT one_shot_hidden" + " ORDER BY report_serial ASC" + " LIMIT $3"); + PREPARE (pg, + "select_reports_desc", + "SELECT" + " report_serial" + " ,report_description" + " ,frequency" + " FROM merchant_reports" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE merchant_instances.merchant_id=$1" + " AND report_serial < $2" + " AND NOT one_shot_hidden" + " ORDER BY report_serial DESC" + " LIMIT $3"); qs = GNUNET_PQ_eval_prepared_multi_select ( pg->conn, (limit > 0) - ? stmt_asc - : stmt_desc, + ? "select_reports_asc" + : "select_reports_desc", params, &select_reports_cb, &plc); diff --git a/src/backenddb/select_unit.c b/src/backenddb/select_unit.c @@ -34,66 +34,23 @@ TALER_MERCHANTDB_select_unit (struct TALER_MERCHANTDB_PostgresContext *pg, { enum GNUNET_DB_QueryStatus qs; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (unit_id), GNUNET_PQ_query_param_end }; - char stmt_custom[PG_PREP_INSTANCE_NAME_MAX]; - char stmt_builtin[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); - check_connection (pg); - PREPARE_INSTANCE (pg, - stmt_custom, - "select_unit_custom", - "SELECT" - " cu.unit_serial" - " ,cu.unit" - " ,cu.unit_name_long" - " ,cu.unit_name_short" - " ,cu.unit_name_long_i18n" - " ,cu.unit_name_short_i18n" - " ,cu.unit_allow_fraction" - " ,cu.unit_precision_level" - " ,cu.unit_active" - " ,FALSE AS unit_builtin" - " FROM merchant_custom_units cu" - " WHERE cu.unit=$1"); - PREPARE_INSTANCE (pg, - stmt_builtin, - "select_unit_builtin", - "SELECT" - " bu.unit_serial" - " ,bu.unit" - " ,bu.unit_name_long" - " ,bu.unit_name_short" - " ,bu.unit_name_long_i18n" - " ,bu.unit_name_short_i18n" - " ,COALESCE(bo.override_allow_fraction, bu.unit_allow_fraction)" - " ,COALESCE(bo.override_precision_level, bu.unit_precision_level)" - " ,COALESCE(bo.override_active, bu.unit_active)" - " ,TRUE AS unit_builtin" - " FROM merchant_builtin_units bu" - " LEFT JOIN merchant_builtin_unit_overrides bo" - " ON bo.builtin_unit_serial = bu.unit_serial" - " WHERE bu.unit=$1"); if (NULL == ud) { struct GNUNET_PQ_ResultSpec rs_null[] = { GNUNET_PQ_result_spec_end }; - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt_custom, - params, - rs_null); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs) - return qs; - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt_builtin, - params, - rs_null); + check_connection (pg); + return GNUNET_PQ_eval_prepared_singleton_select ( + pg->conn, + "select_unit", + params, + rs_null); } else { @@ -121,14 +78,56 @@ TALER_MERCHANTDB_select_unit (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; + check_connection (pg); + + PREPARE (pg, + "select_unit_custom", + "SELECT" + " cu.unit_serial" + " ,cu.unit" + " ,cu.unit_name_long" + " ,cu.unit_name_short" + " ,cu.unit_name_long_i18n" + " ,cu.unit_name_short_i18n" + " ,cu.unit_allow_fraction" + " ,cu.unit_precision_level" + " ,cu.unit_active" + " ,FALSE AS unit_builtin" + " FROM merchant_custom_units cu" + " JOIN merchant_instances inst" + " USING (merchant_serial)" + " WHERE inst.merchant_id=$1" + " AND cu.unit=$2"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt_custom, + "select_unit_custom", params, rs); if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs) return qs; + + PREPARE (pg, + "select_unit_builtin", + "SELECT" + " bu.unit_serial" + " ,bu.unit" + " ,bu.unit_name_long" + " ,bu.unit_name_short" + " ,bu.unit_name_long_i18n" + " ,bu.unit_name_short_i18n" + " ,COALESCE(bo.override_allow_fraction, bu.unit_allow_fraction)" + " ,COALESCE(bo.override_precision_level, bu.unit_precision_level)" + " ,COALESCE(bo.override_active, bu.unit_active)" + " ,TRUE AS unit_builtin" + " FROM merchant_builtin_units bu" + " JOIN merchant_instances inst" + " ON TRUE" + " LEFT JOIN merchant_builtin_unit_overrides bo" + " ON bo.builtin_unit_serial = bu.unit_serial" + " AND bo.merchant_serial = inst.merchant_serial" + " WHERE inst.merchant_id=$1" + " AND bu.unit=$2"); return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt_builtin, + "select_unit_builtin", params, rs); } diff --git a/src/backenddb/select_wirewatch_accounts.c b/src/backenddb/select_wirewatch_accounts.c @@ -71,17 +71,17 @@ handle_results (void *cls, json_t *credential; uint64_t last_serial; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_string ("out_merchant_id", + GNUNET_PQ_result_spec_string ("merchant_id", &instance), - GNUNET_PQ_result_spec_string ("out_payto_uri", + GNUNET_PQ_result_spec_string ("payto_uri", &payto_uri.full_payto), - GNUNET_PQ_result_spec_string ("out_credit_facade_url", + GNUNET_PQ_result_spec_string ("credit_facade_url", &facade_url), GNUNET_PQ_result_spec_allow_null ( - TALER_PQ_result_spec_json ("out_credit_facade_credentials", + TALER_PQ_result_spec_json ("credit_facade_credentials", &credential), NULL), - GNUNET_PQ_result_spec_uint64 ("out_last_bank_serial", + GNUNET_PQ_result_spec_uint64 ("last_bank_serial", &last_serial), GNUNET_PQ_result_spec_end }; @@ -124,12 +124,16 @@ TALER_MERCHANTDB_select_wirewatch_accounts ( PREPARE (pg, "select_wirewatch_progress", "SELECT" - " out_last_bank_serial" - " ,out_merchant_id" - " ,out_payto_uri" - " ,out_credit_facade_url" - " ,out_credit_facade_credentials::TEXT" - " FROM merchant.select_wirewatch_accounts()"); + " last_bank_serial" + ",merchant_id" + ",payto_uri" + ",credit_facade_url" + ",credit_facade_credentials::TEXT" + " FROM merchant_accounts" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE active" + " AND credit_facade_url IS NOT NULL"); check_connection (pg); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, "select_wirewatch_progress", diff --git a/src/backenddb/set_instance.c b/src/backenddb/set_instance.c @@ -1,82 +0,0 @@ -/* - This file is part of TALER - (C) 2026 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 src/backenddb/set_instance.c - * @brief Implementation of TALER_MERCHANTDB_set_instance - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_pq_lib.h> -#include <taler/taler_pq_lib.h> -#include "merchant-database/set_instance.h" -#include "helper.h" - - -enum GNUNET_DB_QueryStatus -TALER_MERCHANTDB_set_instance ( - struct TALER_MERCHANTDB_PostgresContext *pg, - const char *instance_id) -{ - uint64_t serial; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (instance_id), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("merchant_serial", - &serial), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - char sp_sql[128]; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute (sp_sql), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - if ( (NULL != pg->current_merchant_id) && - (0 == strcmp (pg->current_merchant_id, - instance_id)) ) - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; - check_connection (pg); - PREPARE (pg, - "set_instance_lookup_serial", - "SELECT merchant_serial" - " FROM merchant_instances" - " WHERE merchant_id=$1"); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "set_instance_lookup_serial", - params, - rs); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - return qs; - GNUNET_snprintf (sp_sql, - sizeof (sp_sql), - "SET search_path TO merchant_instance_%llu, merchant", - (unsigned long long) serial); - if (GNUNET_OK != - GNUNET_PQ_exec_statements (pg->conn, - es)) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - GNUNET_free (pg->current_merchant_id); - pg->current_merchant_id = GNUNET_strdup (instance_id); - pg->current_merchant_serial = serial; - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -} diff --git a/src/backenddb/solve_mfa_challenge.c b/src/backenddb/solve_mfa_challenge.c @@ -55,23 +55,20 @@ TALER_MERCHANTDB_solve_mfa_challenge ( GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); /* conservatively set security-relevant return values to safe values, even though these should not be used with qs <= 0 */ *solved = false; *retry_counter = 0; - PREPARE_INSTANCE (pg, - stmt, - "solve_mfa_challenge", - "SELECT" - " out_solved" - " ,out_retry_counter" - " FROM merchant_do_solve_mfa_challenge" - " ($1, $2, $3, $4);"); + PREPARE (pg, + "solve_mfa_challenge", + "SELECT" + " out_solved" + " ,out_retry_counter" + " FROM merchant_do_solve_mfa_challenge" + " ($1, $2, $3, $4);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "solve_mfa_challenge", params, rs); if (qs <= 0) diff --git a/src/backenddb/sql-schema/gen-procedures.sh b/src/backenddb/sql-schema/gen-procedures.sh @@ -46,7 +46,6 @@ SET search_path TO merchant; EOF - # Output procedures, stripping comments for x in $@; do @@ -63,48 +62,21 @@ DROP PROCEDURE IF EXISTS merchant_do_gc; CREATE PROCEDURE merchant_do_gc(in_now INT8) LANGUAGE plpgsql AS $$ -DECLARE - rec RECORD; - s TEXT; BEGIN - -- Drop validation-pending instances that never confirmed in time. The - -- AFTER DELETE trigger on merchant.merchant_instances will DROP the - -- per-instance schema for each removed row. - DELETE FROM merchant.merchant_instances + DELETE FROM merchant_instances WHERE validation_needed AND validation_expiration < in_now; - - -- Per-instance GC: loop over all surviving instances and run the - -- per-instance GC helpers + targeted DELETEs in each schema. - FOR rec IN SELECT merchant_serial FROM merchant.merchant_instances - LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - BEGIN - EXECUTE format('SET LOCAL search_path TO %I, merchant', s); - CALL merchant_statistic_amount_gc (); - CALL merchant_statistic_bucket_gc (); - CALL merchant_statistic_counter_gc (); - EXECUTE format('DELETE FROM %I.tan_challenges' - ' WHERE expiration_date < $1', s) USING in_now; - EXECUTE format('DELETE FROM %I.merchant_unclaim_signatures' - ' WHERE expiration_time < $1', s) USING in_now; - EXCEPTION - WHEN undefined_table THEN - NULL; - WHEN undefined_function THEN - NULL; - END; - END LOOP; - -- Restore the global search_path so subsequent statements on this - -- session don't leak into the last visited instance schema. - SET LOCAL search_path TO merchant; + CALL merchant_statistic_amount_gc (); + CALL merchant_statistic_bucket_gc (); + CALL merchant_statistic_counter_gc (); + + DELETE FROM tan_challenges + WHERE expiration_date < in_now; + DELETE FROM merchant_unclaim_signatures + WHERE expiration_time < in_now; END $$; COMMENT ON PROCEDURE merchant_do_gc - IS 'Calls per-instance garbage collection subroutines across every instance.' - ' Removes expired pending-validation instances first (whose ON DELETE' - ' trigger drops the entire per-instance schema), then for each surviving' - ' instance runs merchant_statistic_*_gc and DELETEs expired tan_challenges' - ' / merchant_unclaim_signatures.'; + IS 'calls all other garbage collection subroutines'; COMMIT; EOF diff --git a/src/backenddb/sql-schema/merchant-0036-copy.sql.fragment b/src/backenddb/sql-schema/merchant-0036-copy.sql.fragment @@ -1,504 +0,0 @@ - -- ================================================================= - -- Copy per-instance row data from merchant.* into the new schema. - -- Tables are listed in FK-dependency order so plain INSERT works - -- (no DEFERRABLE games, no WITH ORDINALITY). $1 is bound to - -- rec.merchant_serial via USING. The merchant_serial column is - -- dropped from every column list — the schema name is the - -- discriminator. All target tables use GENERATED BY DEFAULT AS - -- IDENTITY (so we can keep the source serial values), except - -- merchant_login_tokens whose `serial` is GENERATED ALWAYS — that - -- one needs OVERRIDING SYSTEM VALUE. - -- - -- Two stat-meta tables (merchant_statistic_bucket_meta, - -- merchant_statistic_interval_meta) have no merchant_serial in the - -- source schema — they are global slug catalogs and are copied - -- verbatim into every per-instance schema. - -- ================================================================= - - -- ---------------- direct merchant_serial (no JOIN) -------------- - - EXECUTE format('INSERT INTO %I.merchant_accounts' - || ' (account_serial, h_wire, salt, credit_facade_url,' - || ' credit_facade_credentials, last_bank_serial, payto_uri,' - || ' active, extra_wire_subject_metadata)' - || ' SELECT account_serial, h_wire, salt, credit_facade_url,' - || ' credit_facade_credentials, last_bank_serial, payto_uri,' - || ' active, extra_wire_subject_metadata' - || ' FROM merchant.merchant_accounts' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_categories' - || ' (category_serial, category_name, category_name_i18n)' - || ' SELECT category_serial, category_name, category_name_i18n' - || ' FROM merchant.merchant_categories' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_contract_terms' - || ' (order_serial, order_id, contract_terms, wallet_data,' - || ' h_contract_terms, creation_time, pay_deadline, refund_deadline,' - || ' paid, wired, fulfillment_url, session_id, pos_key, pos_algorithm,' - || ' claim_token, choice_index)' - || ' SELECT order_serial, order_id, contract_terms, wallet_data,' - || ' h_contract_terms, creation_time, pay_deadline, refund_deadline,' - || ' paid, wired, fulfillment_url, session_id, pos_key, pos_algorithm,' - || ' claim_token, choice_index' - || ' FROM merchant.merchant_contract_terms' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_custom_units' - || ' (unit_serial, unit, unit_name_long, unit_name_short,' - || ' unit_name_long_i18n, unit_name_short_i18n,' - || ' unit_allow_fraction, unit_precision_level, unit_active)' - || ' SELECT unit_serial, unit, unit_name_long, unit_name_short,' - || ' unit_name_long_i18n, unit_name_short_i18n,' - || ' unit_allow_fraction, unit_precision_level, unit_active' - || ' FROM merchant.merchant_custom_units' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - -- merchant_donau_instances: source filter column is named - -- merchant_instance_serial (not merchant_serial). - EXECUTE format('INSERT INTO %I.merchant_donau_instances' - || ' (donau_instances_serial, donau_url, charity_name, charity_id,' - || ' charity_max_per_year, charity_receipts_to_date, current_year)' - || ' SELECT donau_instances_serial, donau_url, charity_name, charity_id,' - || ' charity_max_per_year, charity_receipts_to_date, current_year' - || ' FROM merchant.merchant_donau_instances' - || ' WHERE merchant_instance_serial = $1', s) - USING rec.merchant_serial; - - -- merchant_login_tokens: target `serial` column is GENERATED ALWAYS - -- → must use OVERRIDING SYSTEM VALUE to preserve serial values. - EXECUTE format('INSERT INTO %I.merchant_login_tokens' - || ' (token, creation_time, expiration_time, validity_scope,' - || ' description, serial)' - || ' OVERRIDING SYSTEM VALUE' - || ' SELECT token, creation_time, expiration_time, validity_scope,' - || ' description, serial' - || ' FROM merchant.merchant_login_tokens' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_money_pots' - || ' (money_pot_serial, money_pot_name, money_pot_description, pot_totals)' - || ' SELECT money_pot_serial, money_pot_name, money_pot_description, pot_totals' - || ' FROM merchant.merchant_money_pots' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_otp_devices' - || ' (otp_serial, otp_id, otp_description, otp_key, otp_algorithm, otp_ctr)' - || ' SELECT otp_serial, otp_id, otp_description, otp_key, otp_algorithm, otp_ctr' - || ' FROM merchant.merchant_otp_devices' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_orders' - || ' (order_serial, order_id, claim_token, h_post_data, pay_deadline,' - || ' creation_time, contract_terms, pos_key, pos_algorithm,' - || ' fulfillment_url, session_id)' - || ' SELECT order_serial, order_id, claim_token, h_post_data, pay_deadline,' - || ' creation_time, contract_terms, pos_key, pos_algorithm,' - || ' fulfillment_url, session_id' - || ' FROM merchant.merchant_orders' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_product_groups' - || ' (product_group_serial, product_group_name, product_group_description)' - || ' SELECT product_group_serial, product_group_name, product_group_description' - || ' FROM merchant.merchant_product_groups' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - -- merchant_inventory references merchant_product_groups and merchant_money_pots - -- (already copied above). - EXECUTE format('INSERT INTO %I.merchant_inventory' - || ' (product_serial, product_id, description, description_i18n,' - || ' unit, image, taxes, total_stock, total_sold, total_lost,' - || ' address, next_restock, minimum_age, product_name, image_hash,' - || ' price_array, total_stock_frac, total_sold_frac, total_lost_frac,' - || ' allow_fractional_quantity, fractional_precision_level,' - || ' product_group_serial, money_pot_serial, price_is_net)' - || ' SELECT product_serial, product_id, description, description_i18n,' - || ' unit, image, taxes, total_stock, total_sold, total_lost,' - || ' address, next_restock, minimum_age, product_name, image_hash,' - || ' price_array, total_stock_frac, total_sold_frac, total_lost_frac,' - || ' allow_fractional_quantity, fractional_precision_level,' - || ' product_group_serial, money_pot_serial, price_is_net' - || ' FROM merchant.merchant_inventory' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_reports' - || ' (report_serial, report_program_section, report_description,' - || ' mime_type, report_token, data_source, target_address,' - || ' frequency, frequency_shift, next_transmission,' - || ' last_error_code, last_error_detail, one_shot_hidden)' - || ' SELECT report_serial, report_program_section, report_description,' - || ' mime_type, report_token, data_source, target_address,' - || ' frequency, frequency_shift, next_transmission,' - || ' last_error_code, last_error_detail, one_shot_hidden' - || ' FROM merchant.merchant_reports' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - -- merchant_template references merchant_otp_devices (already copied). - EXECUTE format('INSERT INTO %I.merchant_template' - || ' (template_serial, template_id, template_description,' - || ' otp_device_id, template_contract, editable_defaults)' - || ' SELECT template_serial, template_id, template_description,' - || ' otp_device_id, template_contract, editable_defaults' - || ' FROM merchant.merchant_template' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_token_families' - || ' (token_family_serial, slug, name, description, description_i18n,' - || ' valid_after, valid_before, duration, kind, issued, used,' - || ' validity_granularity, start_offset, cipher_choice, extra_data)' - || ' SELECT token_family_serial, slug, name, description, description_i18n,' - || ' valid_after, valid_before, duration, kind, issued, used,' - || ' validity_granularity, start_offset, cipher_choice, extra_data' - || ' FROM merchant.merchant_token_families' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_webhook' - || ' (webhook_serial, webhook_id, event_type, url, http_method,' - || ' header_template, body_template)' - || ' SELECT webhook_serial, webhook_id, event_type, url, http_method,' - || ' header_template, body_template' - || ' FROM merchant.merchant_webhook' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - -- merchant_pending_webhooks references merchant_webhook (above) via webhook_serial. - EXECUTE format('INSERT INTO %I.merchant_pending_webhooks' - || ' (webhook_pending_serial, webhook_serial, next_attempt, retries,' - || ' url, http_method, header, body)' - || ' SELECT webhook_pending_serial, webhook_serial, next_attempt, retries,' - || ' url, http_method, header, body' - || ' FROM merchant.merchant_pending_webhooks' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.tan_challenges' - || ' (challenge_id, h_body, salt, op, code, creation_date,' - || ' expiration_date, retransmission_date, confirmation_date,' - || ' retry_counter, tan_channel, required_address)' - || ' SELECT challenge_id, h_body, salt, op, code, creation_date,' - || ' expiration_date, retransmission_date, confirmation_date,' - || ' retry_counter, tan_channel, required_address' - || ' FROM merchant.tan_challenges' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_keys' - || ' (merchant_priv)' - || ' SELECT merchant_priv' - || ' FROM merchant.merchant_keys' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_builtin_unit_overrides' - || ' (builtin_unit_serial, override_allow_fraction,' - || ' override_precision_level, override_active)' - || ' SELECT builtin_unit_serial, override_allow_fraction,' - || ' override_precision_level, override_active' - || ' FROM merchant.merchant_builtin_unit_overrides' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - -- ---------------- statistics: meta tables are GLOBAL ------------ - -- Copied unfiltered (every instance gets a full slug catalog). - - EXECUTE format('INSERT INTO %I.merchant_statistic_bucket_meta' - || ' (bmeta_serial_id, slug, description, stype, ranges, ages)' - || ' SELECT bmeta_serial_id, slug, description, stype, ranges, ages' - || ' FROM merchant.merchant_statistic_bucket_meta', s); - - EXECUTE format('INSERT INTO %I.merchant_statistic_interval_meta' - || ' (imeta_serial_id, slug, description, stype, ranges, precisions)' - || ' SELECT imeta_serial_id, slug, description, stype, ranges, precisions' - || ' FROM merchant.merchant_statistic_interval_meta', s); - - -- ---------------- statistics: per-instance event/bucket tables -- - - EXECUTE format('INSERT INTO %I.merchant_statistic_amount_event' - || ' (aevent_serial_id, imeta_serial_id, slot,' - || ' delta_curr, delta_value, delta_frac)' - || ' SELECT aevent_serial_id, imeta_serial_id, slot,' - || ' delta_curr, delta_value, delta_frac' - || ' FROM merchant.merchant_statistic_amount_event' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_statistic_counter_event' - || ' (nevent_serial_id, imeta_serial_id, slot, delta)' - || ' SELECT nevent_serial_id, imeta_serial_id, slot, delta' - || ' FROM merchant.merchant_statistic_counter_event' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_statistic_bucket_amount' - || ' (bmeta_serial_id, bucket_start, bucket_range, curr,' - || ' cumulative_value, cumulative_frac)' - || ' SELECT bmeta_serial_id, bucket_start, bucket_range, curr,' - || ' cumulative_value, cumulative_frac' - || ' FROM merchant.merchant_statistic_bucket_amount' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_statistic_bucket_counter' - || ' (bmeta_serial_id, bucket_start, bucket_range, cumulative_number)' - || ' SELECT bmeta_serial_id, bucket_start, bucket_range, cumulative_number' - || ' FROM merchant.merchant_statistic_bucket_counter' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_statistic_interval_amount' - || ' (imeta_serial_id, event_delimiter, range, curr,' - || ' cumulative_value, cumulative_frac)' - || ' SELECT imeta_serial_id, event_delimiter, range, curr,' - || ' cumulative_value, cumulative_frac' - || ' FROM merchant.merchant_statistic_interval_amount' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_statistic_interval_counter' - || ' (imeta_serial_id, range, event_delimiter, cumulative_number)' - || ' SELECT imeta_serial_id, range, event_delimiter, cumulative_number' - || ' FROM merchant.merchant_statistic_interval_counter' - || ' WHERE merchant_serial = $1', s) - USING rec.merchant_serial; - - -- ---------------- account-linked (JOIN via account_serial) ------ - - EXECUTE format('INSERT INTO %I.merchant_kyc' - || ' (kyc_serial_id, kyc_timestamp, kyc_ok, account_serial,' - || ' exchange_url, access_token, exchange_http_status,' - || ' exchange_ec_code, aml_review, jaccount_limits,' - || ' last_rule_gen, next_kyc_poll, kyc_backoff)' - || ' SELECT k.kyc_serial_id, k.kyc_timestamp, k.kyc_ok, k.account_serial,' - || ' k.exchange_url, k.access_token, k.exchange_http_status,' - || ' k.exchange_ec_code, k.aml_review, k.jaccount_limits,' - || ' k.last_rule_gen, k.next_kyc_poll, k.kyc_backoff' - || ' FROM merchant.merchant_kyc k' - || ' JOIN merchant.merchant_accounts a' - || ' ON k.account_serial = a.account_serial' - || ' WHERE a.merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_deposit_confirmations' - || ' (deposit_confirmation_serial, order_serial, deposit_timestamp,' - || ' exchange_url, total_without_fee, wire_fee, signkey_serial,' - || ' exchange_sig, account_serial, wire_transfer_deadline,' - || ' wire_pending, exchange_failure, retry_backoff)' - || ' SELECT dc.deposit_confirmation_serial, dc.order_serial, dc.deposit_timestamp,' - || ' dc.exchange_url, dc.total_without_fee, dc.wire_fee, dc.signkey_serial,' - || ' dc.exchange_sig, dc.account_serial, dc.wire_transfer_deadline,' - || ' dc.wire_pending, dc.exchange_failure, dc.retry_backoff' - || ' FROM merchant.merchant_deposit_confirmations dc' - || ' JOIN merchant.merchant_accounts a' - || ' ON dc.account_serial = a.account_serial' - || ' WHERE a.merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_expected_transfers' - || ' (expected_credit_serial, exchange_url, wtid, expected_credit_amount,' - || ' wire_fee, account_serial, expected_time, retry_time, last_http_status,' - || ' last_ec, last_detail, retry_needed, signkey_serial, exchange_sig,' - || ' h_details, confirmed)' - || ' SELECT et.expected_credit_serial, et.exchange_url, et.wtid, et.expected_credit_amount,' - || ' et.wire_fee, et.account_serial, et.expected_time, et.retry_time, et.last_http_status,' - || ' et.last_ec, et.last_detail, et.retry_needed, et.signkey_serial, et.exchange_sig,' - || ' et.h_details, et.confirmed' - || ' FROM merchant.merchant_expected_transfers et' - || ' JOIN merchant.merchant_accounts a' - || ' ON et.account_serial = a.account_serial' - || ' WHERE a.merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_transfers' - || ' (credit_serial, exchange_url, wtid, credit_amount, account_serial,' - || ' bank_serial_id, expected, execution_time)' - || ' SELECT t.credit_serial, t.exchange_url, t.wtid, t.credit_amount, t.account_serial,' - || ' t.bank_serial_id, t.expected, t.execution_time' - || ' FROM merchant.merchant_transfers t' - || ' JOIN merchant.merchant_accounts a' - || ' ON t.account_serial = a.account_serial' - || ' WHERE a.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- ---------------- deposits: deeper join (dc → accounts) --------- - - EXECUTE format('INSERT INTO %I.merchant_deposits' - || ' (deposit_serial, coin_offset, deposit_confirmation_serial,' - || ' coin_pub, coin_sig, amount_with_fee, deposit_fee, refund_fee,' - || ' settlement_retry_needed, settlement_retry_time,' - || ' settlement_last_http_status, settlement_last_ec,' - || ' settlement_last_detail, settlement_wtid,' - || ' settlement_coin_contribution, settlement_expected_credit_serial,' - || ' signkey_serial, settlement_exchange_sig)' - || ' SELECT d.deposit_serial, d.coin_offset, d.deposit_confirmation_serial,' - || ' d.coin_pub, d.coin_sig, d.amount_with_fee, d.deposit_fee, d.refund_fee,' - || ' d.settlement_retry_needed, d.settlement_retry_time,' - || ' d.settlement_last_http_status, d.settlement_last_ec,' - || ' d.settlement_last_detail, d.settlement_wtid,' - || ' d.settlement_coin_contribution, d.settlement_expected_credit_serial,' - || ' d.signkey_serial, d.settlement_exchange_sig' - || ' FROM merchant.merchant_deposits d' - || ' JOIN merchant.merchant_deposit_confirmations dc' - || ' ON d.deposit_confirmation_serial = dc.deposit_confirmation_serial' - || ' JOIN merchant.merchant_accounts a' - || ' ON dc.account_serial = a.account_serial' - || ' WHERE a.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- expected_transfer_to_coin: deposit_serial → deposits → dc → accounts. - EXECUTE format('INSERT INTO %I.merchant_expected_transfer_to_coin' - || ' (deposit_serial, expected_credit_serial, offset_in_exchange_list,' - || ' exchange_deposit_value, exchange_deposit_fee)' - || ' SELECT ettc.deposit_serial, ettc.expected_credit_serial, ettc.offset_in_exchange_list,' - || ' ettc.exchange_deposit_value, ettc.exchange_deposit_fee' - || ' FROM merchant.merchant_expected_transfer_to_coin ettc' - || ' JOIN merchant.merchant_deposits d' - || ' ON ettc.deposit_serial = d.deposit_serial' - || ' JOIN merchant.merchant_deposit_confirmations dc' - || ' ON d.deposit_confirmation_serial = dc.deposit_confirmation_serial' - || ' JOIN merchant.merchant_accounts a' - || ' ON dc.account_serial = a.account_serial' - || ' WHERE a.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- transfer_signatures: expected_credit_serial → expected_transfers → accounts. - EXECUTE format('INSERT INTO %I.merchant_transfer_signatures' - || ' (expected_credit_serial, signkey_serial, wire_fee, credit_amount,' - || ' execution_time, exchange_sig)' - || ' SELECT ts.expected_credit_serial, ts.signkey_serial, ts.wire_fee, ts.credit_amount,' - || ' ts.execution_time, ts.exchange_sig' - || ' FROM merchant.merchant_transfer_signatures ts' - || ' JOIN merchant.merchant_expected_transfers et' - || ' ON ts.expected_credit_serial = et.expected_credit_serial' - || ' JOIN merchant.merchant_accounts a' - || ' ON et.account_serial = a.account_serial' - || ' WHERE a.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- ---------------- order/contract-linked (JOIN via order_serial) --- - - EXECUTE format('INSERT INTO %I.merchant_refunds' - || ' (refund_serial, order_serial, rtransaction_id, refund_timestamp,' - || ' coin_pub, reason, refund_amount)' - || ' SELECT r.refund_serial, r.order_serial, r.rtransaction_id, r.refund_timestamp,' - || ' r.coin_pub, r.reason, r.refund_amount' - || ' FROM merchant.merchant_refunds r' - || ' JOIN merchant.merchant_contract_terms ct' - || ' ON r.order_serial = ct.order_serial' - || ' WHERE ct.merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_refund_proofs' - || ' (refund_serial, exchange_sig, signkey_serial)' - || ' SELECT rp.refund_serial, rp.exchange_sig, rp.signkey_serial' - || ' FROM merchant.merchant_refund_proofs rp' - || ' JOIN merchant.merchant_refunds r' - || ' ON rp.refund_serial = r.refund_serial' - || ' JOIN merchant.merchant_contract_terms ct' - || ' ON r.order_serial = ct.order_serial' - || ' WHERE ct.merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_order_token_blinded_sigs' - || ' (order_token_bs_serial, order_serial, token_index,' - || ' token_blinded_signature, token_hash)' - || ' SELECT otbs.order_token_bs_serial, otbs.order_serial, otbs.token_index,' - || ' otbs.token_blinded_signature, otbs.token_hash' - || ' FROM merchant.merchant_order_token_blinded_sigs otbs' - || ' JOIN merchant.merchant_contract_terms ct' - || ' ON otbs.order_serial = ct.order_serial' - || ' WHERE ct.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- unclaim_signatures: linked via h_contract_terms. - EXECUTE format('INSERT INTO %I.merchant_unclaim_signatures' - || ' (unclaim_serial, h_contract_terms, unclaim_sig, expiration_time)' - || ' SELECT us.unclaim_serial, us.h_contract_terms, us.unclaim_sig, us.expiration_time' - || ' FROM merchant.merchant_unclaim_signatures us' - || ' JOIN merchant.merchant_contract_terms ct' - || ' ON us.h_contract_terms = ct.h_contract_terms' - || ' WHERE ct.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- order_locks: order_serial → contract_terms. - EXECUTE format('INSERT INTO %I.merchant_order_locks' - || ' (product_serial, total_locked, order_serial, total_locked_frac)' - || ' SELECT ol.product_serial, ol.total_locked, ol.order_serial, ol.total_locked_frac' - || ' FROM merchant.merchant_order_locks ol' - || ' JOIN merchant.merchant_contract_terms ct' - || ' ON ol.order_serial = ct.order_serial' - || ' WHERE ct.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- inventory_locks: product_serial → inventory. - EXECUTE format('INSERT INTO %I.merchant_inventory_locks' - || ' (product_serial, lock_uuid, total_locked, expiration, total_locked_frac)' - || ' SELECT il.product_serial, il.lock_uuid, il.total_locked, il.expiration, il.total_locked_frac' - || ' FROM merchant.merchant_inventory_locks il' - || ' JOIN merchant.merchant_inventory i' - || ' ON il.product_serial = i.product_serial' - || ' WHERE i.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- product_categories: junction; pivot on product_serial → inventory. - EXECUTE format('INSERT INTO %I.merchant_product_categories' - || ' (category_serial, product_serial)' - || ' SELECT pc.category_serial, pc.product_serial' - || ' FROM merchant.merchant_product_categories pc' - || ' JOIN merchant.merchant_inventory i' - || ' ON pc.product_serial = i.product_serial' - || ' WHERE i.merchant_serial = $1', s) - USING rec.merchant_serial; - - -- ---------------- token chain (token_families → keys → tokens) -- - - EXECUTE format('INSERT INTO %I.merchant_token_family_keys' - || ' (token_family_key_serial, token_family_serial, pub, h_pub,' - || ' priv, cipher, signature_validity_start, signature_validity_end,' - || ' private_key_deleted_at, private_key_created_at)' - || ' SELECT tfk.token_family_key_serial, tfk.token_family_serial, tfk.pub, tfk.h_pub,' - || ' tfk.priv, tfk.cipher, tfk.signature_validity_start, tfk.signature_validity_end,' - || ' tfk.private_key_deleted_at, tfk.private_key_created_at' - || ' FROM merchant.merchant_token_family_keys tfk' - || ' JOIN merchant.merchant_token_families tf' - || ' ON tfk.token_family_serial = tf.token_family_serial' - || ' WHERE tf.merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_issued_tokens' - || ' (issued_token_serial, h_contract_terms, token_family_key_serial, blind_sig)' - || ' SELECT it.issued_token_serial, it.h_contract_terms, it.token_family_key_serial, it.blind_sig' - || ' FROM merchant.merchant_issued_tokens it' - || ' JOIN merchant.merchant_token_family_keys tfk' - || ' ON it.token_family_key_serial = tfk.token_family_key_serial' - || ' JOIN merchant.merchant_token_families tf' - || ' ON tfk.token_family_serial = tf.token_family_serial' - || ' WHERE tf.merchant_serial = $1', s) - USING rec.merchant_serial; - - EXECUTE format('INSERT INTO %I.merchant_used_tokens' - || ' (spent_token_serial, h_contract_terms, token_family_key_serial,' - || ' token_pub, token_sig, blind_sig)' - || ' SELECT ut.spent_token_serial, ut.h_contract_terms, ut.token_family_key_serial,' - || ' ut.token_pub, ut.token_sig, ut.blind_sig' - || ' FROM merchant.merchant_used_tokens ut' - || ' JOIN merchant.merchant_token_family_keys tfk' - || ' ON ut.token_family_key_serial = tfk.token_family_key_serial' - || ' JOIN merchant.merchant_token_families tf' - || ' ON tfk.token_family_serial = tf.token_family_serial' - || ' WHERE tf.merchant_serial = $1', s) - USING rec.merchant_serial; diff --git a/src/backenddb/sql-schema/merchant-0036-drop.sql.fragment b/src/backenddb/sql-schema/merchant-0036-drop.sql.fragment @@ -1,50 +0,0 @@ --- Drop the now-empty per-instance tables from the merchant schema. --- CASCADE removes the (now obsolete) trigger attachments along with them. --- The per-instance trigger functions in merchant.* (handle_category_changes, --- merchant_*_statistics_trigger, ...) are kept around — they will be replaced --- by the procedures.sql reload after the patch finishes. - -DROP TABLE merchant.merchant_unclaim_signatures CASCADE; -DROP TABLE merchant.merchant_used_tokens CASCADE; -DROP TABLE merchant.merchant_issued_tokens CASCADE; -DROP TABLE merchant.merchant_token_family_keys CASCADE; -DROP TABLE merchant.merchant_token_families CASCADE; -DROP TABLE merchant.merchant_pending_webhooks CASCADE; -DROP TABLE merchant.merchant_webhook CASCADE; -DROP TABLE merchant.merchant_transfer_signatures CASCADE; -DROP TABLE merchant.merchant_expected_transfer_to_coin CASCADE; -DROP TABLE merchant.merchant_transfers CASCADE; -DROP TABLE merchant.merchant_expected_transfers CASCADE; -DROP TABLE merchant.merchant_kyc CASCADE; -DROP TABLE merchant.merchant_deposits CASCADE; -DROP TABLE merchant.merchant_deposit_confirmations CASCADE; -DROP TABLE merchant.merchant_refund_proofs CASCADE; -DROP TABLE merchant.merchant_refunds CASCADE; -DROP TABLE merchant.merchant_order_token_blinded_sigs CASCADE; -DROP TABLE merchant.merchant_order_locks CASCADE; -DROP TABLE merchant.merchant_inventory_locks CASCADE; -DROP TABLE merchant.merchant_product_categories CASCADE; -DROP TABLE merchant.merchant_template CASCADE; -DROP TABLE merchant.merchant_otp_devices CASCADE; -DROP TABLE merchant.merchant_inventory CASCADE; -DROP TABLE merchant.merchant_product_groups CASCADE; -DROP TABLE merchant.merchant_money_pots CASCADE; -DROP TABLE merchant.merchant_categories CASCADE; -DROP TABLE merchant.merchant_contract_terms CASCADE; -DROP TABLE merchant.merchant_orders CASCADE; -DROP TABLE merchant.merchant_custom_units CASCADE; -DROP TABLE merchant.merchant_builtin_unit_overrides CASCADE; -DROP TABLE merchant.merchant_login_tokens CASCADE; -DROP TABLE merchant.merchant_donau_instances CASCADE; -DROP TABLE merchant.merchant_reports CASCADE; -DROP TABLE merchant.merchant_keys CASCADE; -DROP TABLE merchant.merchant_accounts CASCADE; -DROP TABLE merchant.tan_challenges CASCADE; -DROP TABLE merchant.merchant_statistic_amount_event CASCADE; -DROP TABLE merchant.merchant_statistic_counter_event CASCADE; -DROP TABLE merchant.merchant_statistic_interval_amount CASCADE; -DROP TABLE merchant.merchant_statistic_interval_counter CASCADE; -DROP TABLE merchant.merchant_statistic_interval_meta CASCADE; -DROP TABLE merchant.merchant_statistic_bucket_amount CASCADE; -DROP TABLE merchant.merchant_statistic_bucket_counter CASCADE; -DROP TABLE merchant.merchant_statistic_bucket_meta CASCADE; diff --git a/src/backenddb/sql-schema/merchant-0036-setval.sql.fragment b/src/backenddb/sql-schema/merchant-0036-setval.sql.fragment @@ -1,35 +0,0 @@ - -- Advance every IDENTITY sequence in the new instance schema past the - -- maximum value just copied so future inserts do not collide. - PERFORM setval(pg_get_serial_sequence(s || '.merchant_accounts', 'account_serial'), COALESCE((SELECT MAX(account_serial) FROM merchant.merchant_accounts WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_categories', 'category_serial'), COALESCE((SELECT MAX(category_serial) FROM merchant.merchant_categories WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_contract_terms', 'order_serial'), COALESCE((SELECT MAX(order_serial) FROM merchant.merchant_contract_terms WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_custom_units', 'unit_serial'), COALESCE((SELECT MAX(unit_serial) FROM merchant.merchant_custom_units WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_donau_instances', 'donau_instances_serial'), COALESCE((SELECT MAX(donau_instances_serial) FROM merchant.merchant_donau_instances WHERE merchant_instance_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_inventory', 'product_serial'), COALESCE((SELECT MAX(product_serial) FROM merchant.merchant_inventory WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_login_tokens', 'serial'), COALESCE((SELECT MAX(serial) FROM merchant.merchant_login_tokens WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_money_pots', 'money_pot_serial'), COALESCE((SELECT MAX(money_pot_serial) FROM merchant.merchant_money_pots WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_orders', 'order_serial'), COALESCE((SELECT MAX(order_serial) FROM merchant.merchant_orders WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_otp_devices', 'otp_serial'), COALESCE((SELECT MAX(otp_serial) FROM merchant.merchant_otp_devices WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_pending_webhooks', 'webhook_pending_serial'), COALESCE((SELECT MAX(webhook_pending_serial) FROM merchant.merchant_pending_webhooks WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_product_groups', 'product_group_serial'), COALESCE((SELECT MAX(product_group_serial) FROM merchant.merchant_product_groups WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_reports', 'report_serial'), COALESCE((SELECT MAX(report_serial) FROM merchant.merchant_reports WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_statistic_amount_event', 'aevent_serial_id'), COALESCE((SELECT MAX(aevent_serial_id) FROM merchant.merchant_statistic_amount_event WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_statistic_bucket_meta', 'bmeta_serial_id'), COALESCE((SELECT MAX(bmeta_serial_id) FROM merchant.merchant_statistic_bucket_meta), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_statistic_counter_event', 'nevent_serial_id'), COALESCE((SELECT MAX(nevent_serial_id) FROM merchant.merchant_statistic_counter_event WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_statistic_interval_meta', 'imeta_serial_id'), COALESCE((SELECT MAX(imeta_serial_id) FROM merchant.merchant_statistic_interval_meta), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_template', 'template_serial'), COALESCE((SELECT MAX(template_serial) FROM merchant.merchant_template WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_token_families', 'token_family_serial'), COALESCE((SELECT MAX(token_family_serial) FROM merchant.merchant_token_families WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_webhook', 'webhook_serial'), COALESCE((SELECT MAX(webhook_serial) FROM merchant.merchant_webhook WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.tan_challenges', 'challenge_id'), COALESCE((SELECT MAX(challenge_id) FROM merchant.tan_challenges WHERE merchant_serial = rec.merchant_serial), 0) + 1, false); - -- Indirect-FK tables (owning instance via JOIN) - PERFORM setval(pg_get_serial_sequence(s || '.merchant_kyc', 'kyc_serial_id'), COALESCE((SELECT MAX(k.kyc_serial_id) FROM merchant.merchant_kyc k JOIN merchant.merchant_accounts a ON k.account_serial = a.account_serial WHERE a.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_deposit_confirmations', 'deposit_confirmation_serial'), COALESCE((SELECT MAX(dc.deposit_confirmation_serial) FROM merchant.merchant_deposit_confirmations dc JOIN merchant.merchant_accounts a ON dc.account_serial = a.account_serial WHERE a.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_expected_transfers', 'expected_credit_serial'), COALESCE((SELECT MAX(et.expected_credit_serial) FROM merchant.merchant_expected_transfers et JOIN merchant.merchant_accounts a ON et.account_serial = a.account_serial WHERE a.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_transfers', 'credit_serial'), COALESCE((SELECT MAX(t.credit_serial) FROM merchant.merchant_transfers t JOIN merchant.merchant_accounts a ON t.account_serial = a.account_serial WHERE a.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_deposits', 'deposit_serial'), COALESCE((SELECT MAX(d.deposit_serial) FROM merchant.merchant_deposits d JOIN merchant.merchant_deposit_confirmations dc ON d.deposit_confirmation_serial = dc.deposit_confirmation_serial JOIN merchant.merchant_accounts a ON dc.account_serial = a.account_serial WHERE a.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_refunds', 'refund_serial'), COALESCE((SELECT MAX(r.refund_serial) FROM merchant.merchant_refunds r JOIN merchant.merchant_contract_terms ct ON r.order_serial = ct.order_serial WHERE ct.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_token_family_keys', 'token_family_key_serial'), COALESCE((SELECT MAX(tfk.token_family_key_serial) FROM merchant.merchant_token_family_keys tfk JOIN merchant.merchant_token_families tf ON tfk.token_family_serial = tf.token_family_serial WHERE tf.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_issued_tokens', 'issued_token_serial'), COALESCE((SELECT MAX(it.issued_token_serial) FROM merchant.merchant_issued_tokens it JOIN merchant.merchant_token_family_keys tfk ON it.token_family_key_serial = tfk.token_family_key_serial JOIN merchant.merchant_token_families tf ON tfk.token_family_serial = tf.token_family_serial WHERE tf.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_used_tokens', 'spent_token_serial'), COALESCE((SELECT MAX(ut.spent_token_serial) FROM merchant.merchant_used_tokens ut JOIN merchant.merchant_token_family_keys tfk ON ut.token_family_key_serial = tfk.token_family_key_serial JOIN merchant.merchant_token_families tf ON tfk.token_family_serial = tf.token_family_serial WHERE tf.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_unclaim_signatures', 'unclaim_serial'), COALESCE((SELECT MAX(us.unclaim_serial) FROM merchant.merchant_unclaim_signatures us JOIN merchant.merchant_contract_terms ct ON us.h_contract_terms = ct.h_contract_terms WHERE ct.merchant_serial = rec.merchant_serial), 0) + 1, false); - PERFORM setval(pg_get_serial_sequence(s || '.merchant_order_token_blinded_sigs','order_token_bs_serial'), COALESCE((SELECT MAX(otbs.order_token_bs_serial) FROM merchant.merchant_order_token_blinded_sigs otbs JOIN merchant.merchant_contract_terms ct ON otbs.order_serial = ct.order_serial WHERE ct.merchant_serial = rec.merchant_serial), 0) + 1, false); diff --git a/src/backenddb/sql-schema/merchant-0036.sql.in b/src/backenddb/sql-schema/merchant-0036.sql.in @@ -1,88 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2026 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU 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 merchant-0036.sql --- @brief Move per-instance tables into per-instance schemas --- merchant_instance_<merchant_serial>. Each existing row of --- merchant_instances is converted by calling --- merchant.create_instance_schema(serial), copying the rows of every --- per-instance table into the new schema (dropping the merchant_serial --- column), and finally dropping the now-empty merchant.<table>. --- @author Christian Grothoff - -BEGIN; - -SELECT _v.register_patch('merchant-0036', NULL, NULL); - -SET search_path TO merchant; - --- --------------------------------------------------------------------- --- Step 1: Install the schema constructor. --- procedures.sql will re-install the same definition (DROP IF EXISTS), --- so installing it here is harmless and lets the migration call it. --- --------------------------------------------------------------------- -#include "../pg_create_instance_schema.sql" - --- --------------------------------------------------------------------- --- Step 2: Build a per-instance schema for every existing instance. --- --------------------------------------------------------------------- -DO $MIG$ -DECLARE - rec RECORD; -BEGIN - FOR rec IN SELECT merchant_serial FROM merchant.merchant_instances LOOP - PERFORM merchant.create_instance_schema(rec.merchant_serial); - END LOOP; -END $MIG$; - --- --------------------------------------------------------------------- --- Step 3: Migrate row data per instance. --- Tables are copied in FK-dependency order. Every copy drops the --- merchant_serial column. After each table is copied, its IDENTITY --- sequence (if any) is advanced past MAX(serial). --- Indirect-FK tables (merchant_kyc, merchant_deposits, ...) are copied --- by JOINing back to the table that owns the merchant_serial. --- --------------------------------------------------------------------- --- Suppress USER triggers during the bulk copy: per-instance triggers --- (e.g. merchant_kyc_insert_trigger) would otherwise fire on every --- inserted row and CALL into per-instance procedures that may rely on --- runtime invariants we do not have during migration. This setting is --- transaction-local and does not affect normal callers. -SET session_replication_role = replica; - -DO $MIG$ -DECLARE - rec RECORD; - s TEXT; -BEGIN - FOR rec IN SELECT merchant_serial FROM merchant.merchant_instances LOOP - s := 'merchant_instance_' || rec.merchant_serial::TEXT; - -#include "merchant-0036-copy.sql.fragment" - -#include "merchant-0036-setval.sql.fragment" - - END LOOP; -END $MIG$; - -SET session_replication_role = origin; - --- --------------------------------------------------------------------- --- Step 4: Drop old per-instance tables from the merchant schema, in --- reverse FK-dependency order so CASCADE is not needed. --- --------------------------------------------------------------------- -#include "merchant-0036-drop.sql.fragment" - -COMMIT; diff --git a/src/backenddb/sql-schema/meson.build b/src/backenddb/sql-schema/meson.build @@ -66,7 +66,6 @@ sqlfiles = [ 'merchant-0033.sql', 'merchant-0034.sql', 'merchant-0035.sql', - 'merchant-0036.sql', 'versioning.sql', ] diff --git a/src/backenddb/unlock_inventory.c b/src/backenddb/unlock_inventory.c @@ -34,17 +34,13 @@ TALER_MERCHANTDB_unlock_inventory (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "unlock_inventory", - "DELETE" - " FROM merchant_inventory_locks" - " WHERE lock_uuid=$1"); + PREPARE (pg, + "unlock_inventory", + "DELETE" + " FROM merchant_inventory_locks" + " WHERE lock_uuid=$1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "unlock_inventory", params); } diff --git a/src/backenddb/update_account.c b/src/backenddb/update_account.c @@ -35,6 +35,7 @@ TALER_MERCHANTDB_update_account (struct TALER_MERCHANTDB_PostgresContext *pg, const json_t *credit_facade_credentials) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (id), GNUNET_PQ_query_param_auto_from_type (h_wire), NULL == credit_facade_url ? GNUNET_PQ_query_param_null () @@ -47,22 +48,21 @@ TALER_MERCHANTDB_update_account (struct TALER_MERCHANTDB_PostgresContext *pg, : GNUNET_PQ_query_param_string (extra_wire_subject_metadata), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_account", - "UPDATE merchant_accounts SET" - " credit_facade_url=$2" - ",credit_facade_credentials=" - " COALESCE($3::TEXT::JSONB, credit_facade_credentials)" - ",extra_wire_subject_metadata=$4" - " WHERE h_wire=$1;"); + PREPARE (pg, + "update_account", + "UPDATE merchant_accounts SET" + " credit_facade_url=$3" + ",credit_facade_credentials=" + " COALESCE($4::TEXT::JSONB, credit_facade_credentials)" + ",extra_wire_subject_metadata=$5" + " WHERE h_wire=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_account", params); } diff --git a/src/backenddb/update_category.c b/src/backenddb/update_category.c @@ -34,25 +34,25 @@ TALER_MERCHANTDB_update_category (struct TALER_MERCHANTDB_PostgresContext *pg, const json_t *category_name_i18n) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&category_id), GNUNET_PQ_query_param_string (category_name), TALER_PQ_query_param_json (category_name_i18n), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_category", - "UPDATE merchant_categories SET" - " category_name=$2" - ",category_name_i18n=$3::TEXT::JSONB" - " WHERE category_serial=$1"); + PREPARE (pg, + "update_category", + "UPDATE merchant_categories SET" + " category_name=$3" + ",category_name_i18n=$4::TEXT::JSONB" + " WHERE merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND category_serial=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_category", params); } diff --git a/src/backenddb/update_contract_session.c b/src/backenddb/update_contract_session.c @@ -35,6 +35,7 @@ TALER_MERCHANTDB_update_contract_session (struct TALER_MERCHANTDB_PostgresContex bool *refunded) { 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_string (session_id), GNUNET_PQ_query_param_end @@ -48,13 +49,9 @@ TALER_MERCHANTDB_update_contract_session (struct TALER_MERCHANTDB_PostgresContex refunded), GNUNET_PQ_result_spec_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; /* Session ID must always be given by the caller. */ GNUNET_assert (NULL != session_id); - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); /* no preflight check here, run in transaction by caller! */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -62,21 +59,24 @@ TALER_MERCHANTDB_update_contract_session (struct TALER_MERCHANTDB_PostgresContex GNUNET_h2s (&h_contract_terms->hash), instance_id, session_id); - PREPARE_INSTANCE (pg, - stmt, - "update_contract_session", - "UPDATE merchant_contract_terms mct SET" - " session_id=$2" - " WHERE h_contract_terms=$1" - " RETURNING" - " contract_terms->>'fulfillment_url' AS fulfillment_url" - " ,EXISTS (SELECT 1" - " FROM merchant_refunds mr" - " WHERE order_serial=mct.order_serial" - " ) AS refunded"); + PREPARE (pg, + "update_contract_session", + "UPDATE merchant_contract_terms mct SET" + " session_id=$3" + " WHERE h_contract_terms=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " RETURNING" + " contract_terms->>'fulfillment_url' AS fulfillment_url" + " ,EXISTS (SELECT 1" + " FROM merchant_refunds mr" + " WHERE order_serial=mct.order_serial" + " ) AS refunded"); *fulfillment_url = NULL; return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "update_contract_session", params, rs); } diff --git a/src/backenddb/update_contract_terms.c b/src/backenddb/update_contract_terms.c @@ -71,12 +71,10 @@ TALER_MERCHANTDB_update_contract_terms (struct TALER_MERCHANTDB_PostgresContext } } - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (order_id), TALER_PQ_query_param_json (contract_terms), GNUNET_PQ_query_param_auto_from_type (&h_contract_terms), @@ -87,20 +85,21 @@ TALER_MERCHANTDB_update_contract_terms (struct TALER_MERCHANTDB_PostgresContext : GNUNET_PQ_query_param_string (fulfillment_url), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - PREPARE_INSTANCE (pg, - stmt, - "update_contract_terms", - "UPDATE merchant_contract_terms SET" - " contract_terms=$2::TEXT::JSONB" - ",h_contract_terms=$3" - ",pay_deadline=$4" - ",refund_deadline=$5" - ",fulfillment_url=$6" - " WHERE order_id=$1"); + PREPARE (pg, + "update_contract_terms", + "UPDATE merchant_contract_terms SET" + " contract_terms=$3::TEXT::JSONB" + ",h_contract_terms=$4" + ",pay_deadline=$5" + ",refund_deadline=$6" + ",fulfillment_url=$7" + " WHERE order_id=$2" + " AND merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_contract_terms", params); } } diff --git a/src/backenddb/update_deposit_confirmation_status.c b/src/backenddb/update_deposit_confirmation_status.c @@ -48,22 +48,18 @@ TALER_MERCHANTDB_update_deposit_confirmation_status (struct TALER_MERCHANTDB_Pos GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_deposit_confirmation_status", - "UPDATE merchant_deposits SET" - " settlement_retry_needed=$2" - ",settlement_retry_time=$3" - ",settlement_last_http_status=$4" - ",settlement_last_ec=$5" - ",settlement_last_detail=$6" - " WHERE deposit_serial=$1;"); + PREPARE (pg, + "update_deposit_confirmation_status", + "UPDATE merchant_deposits SET" + " settlement_retry_needed=$2" + ",settlement_retry_time=$3" + ",settlement_last_http_status=$4" + ",settlement_last_ec=$5" + ",settlement_last_detail=$6" + " WHERE deposit_serial=$1;"); return GNUNET_PQ_eval_prepared_non_select ( pg->conn, - stmt, + "update_deposit_confirmation_status", params); } diff --git a/src/backenddb/update_donau_instance.c b/src/backenddb/update_donau_instance.c @@ -37,6 +37,7 @@ TALER_MERCHANTDB_update_donau_instance (struct TALER_MERCHANTDB_PostgresContext struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_string (donau_url), GNUNET_PQ_query_param_string (charity->name), + GNUNET_PQ_query_param_auto_from_type (&charity->charity_pub), GNUNET_PQ_query_param_uint64 (&charity_id), TALER_PQ_query_param_amount_with_currency (pg->conn, &charity->max_per_year), @@ -45,23 +46,24 @@ TALER_MERCHANTDB_update_donau_instance (struct TALER_MERCHANTDB_PostgresContext GNUNET_PQ_query_param_uint64 (&charity->current_year), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_existing_donau_instance", - "UPDATE merchant_donau_instances SET" - " charity_name = $2," - " charity_max_per_year = $4," - " charity_receipts_to_date = $5," - " current_year = $6" - " WHERE charity_id = $3" - " AND donau_url = $1;"); + PREPARE (pg, + "update_existing_donau_instance", + "UPDATE merchant_donau_instances SET" + " charity_name = $2," + " charity_max_per_year = $5," + " charity_receipts_to_date = $6," + " current_year = $7" + " WHERE charity_id = $4" + " AND merchant_instance_serial " + " = (SELECT merchant_serial" + " FROM merchant_instances mi" + " WHERE mi.merchant_pub = $3)" + " AND donau_url = $1;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_existing_donau_instance", params); } diff --git a/src/backenddb/update_donau_instance_receipts_amount.c b/src/backenddb/update_donau_instance_receipts_amount.c @@ -40,19 +40,15 @@ TALER_MERCHANTDB_update_donau_instance_receipts_amount (struct TALER_MERCHANTDB_ GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_donau_instance_receipts", - "UPDATE merchant_donau_instances " - "SET charity_receipts_to_date = $2 " - "WHERE donau_instances_serial = $1;"); + PREPARE (pg, + "update_donau_instance_receipts", + "UPDATE merchant_donau_instances " + "SET charity_receipts_to_date = $2 " + "WHERE donau_instances_serial = $1;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_donau_instance_receipts", params); } diff --git a/src/backenddb/update_mfa_challenge.c b/src/backenddb/update_mfa_challenge.c @@ -42,20 +42,17 @@ TALER_MERCHANTDB_update_mfa_challenge (struct TALER_MERCHANTDB_PostgresContext * GNUNET_PQ_query_param_absolute_time (&retransmission_date), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - PREPARE_INSTANCE (pg, - stmt, - "update_mfa_challenge", - "UPDATE tan_challenges" - " SET" - " code=$2" - " ,retry_counter=$3" - " ,expiration_date=$4" - " ,retransmission_date=$5" - " WHERE challenge_id = $1;"); + PREPARE (pg, + "update_mfa_challenge", + "UPDATE tan_challenges" + " SET" + " code=$2" + " ,retry_counter=$3" + " ,expiration_date=$4" + " ,retransmission_date=$5" + " WHERE challenge_id = $1;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_mfa_challenge", params); } diff --git a/src/backenddb/update_money_pot.c b/src/backenddb/update_money_pot.c @@ -40,6 +40,7 @@ TALER_MERCHANTDB_update_money_pot (struct TALER_MERCHANTDB_PostgresContext *pg, bool *conflict_name) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&money_pot_id), GNUNET_PQ_query_param_string (name), GNUNET_PQ_query_param_string (description), @@ -66,23 +67,18 @@ TALER_MERCHANTDB_update_money_pot (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_money_pot", - "SELECT" - " out_conflict_name AS conflict_name" - ",out_conflict_total AS conflict_total" - ",out_not_found AS not_found" - " FROM merchant_do_update_money_pot" - "($1,$2,$3,$4,$5);"); + PREPARE (pg, + "update_money_pot", + "SELECT" + " out_conflict_name AS conflict_name" + ",out_conflict_total AS conflict_total" + ",out_not_found AS not_found" + " FROM merchant_do_update_money_pot" + "($1,$2,$3,$4,$5,$6);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "update_money_pot", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/update_otp.c b/src/backenddb/update_otp.c @@ -34,6 +34,7 @@ TALER_MERCHANTDB_update_otp (struct TALER_MERCHANTDB_PostgresContext *pg, { uint32_t pos32 = (uint32_t) td->otp_algorithm; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (otp_id), GNUNET_PQ_query_param_string (td->otp_description), GNUNET_PQ_query_param_uint32 (&pos32), @@ -43,22 +44,21 @@ TALER_MERCHANTDB_update_otp (struct TALER_MERCHANTDB_PostgresContext *pg, : GNUNET_PQ_query_param_string (td->otp_key), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_otp", - "UPDATE merchant_otp_devices SET" - " otp_description=$2" - ",otp_algorithm=$3" - ",otp_ctr=$4" - ",otp_key=COALESCE($5,otp_key)" - " WHERE otp_id=$1"); + PREPARE (pg, + "update_otp", + "UPDATE merchant_otp_devices SET" + " otp_description=$3" + ",otp_algorithm=$4" + ",otp_ctr=$5" + ",otp_key=COALESCE($6,otp_key)" + " WHERE merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND otp_id=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_otp", params); } diff --git a/src/backenddb/update_pending_webhook.c b/src/backenddb/update_pending_webhook.c @@ -36,19 +36,15 @@ TALER_MERCHANTDB_update_pending_webhook (struct TALER_MERCHANTDB_PostgresContext GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_pending_webhook", - "UPDATE merchant_pending_webhooks SET" - " retries=retries+1" - ",next_attempt=$2" - " WHERE webhook_pending_serial=$1"); + PREPARE (pg, + "update_pending_webhook", + "UPDATE merchant_pending_webhooks SET" + " retries=retries+1" + ",next_attempt=$2" + " WHERE webhook_pending_serial=$1"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_pending_webhook", params); } diff --git a/src/backenddb/update_product.c b/src/backenddb/update_product.c @@ -44,40 +44,43 @@ TALER_MERCHANTDB_update_product (struct TALER_MERCHANTDB_PostgresContext *pg, bool *no_pot) { struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (product_id), /* $1 */ + GNUNET_PQ_query_param_string (instance_id), /* $1 */ + GNUNET_PQ_query_param_string (product_id), GNUNET_PQ_query_param_string (pd->description), - TALER_PQ_query_param_json (pd->description_i18n), /* $3 */ + TALER_PQ_query_param_json (pd->description_i18n), /* $4 */ GNUNET_PQ_query_param_string (pd->unit), - GNUNET_PQ_query_param_string (pd->image), /* $5 */ - TALER_PQ_query_param_json (pd->taxes), /* $6 */ + GNUNET_PQ_query_param_string (pd->image), /* $6 */ + TALER_PQ_query_param_json (pd->taxes), /* $7 */ TALER_PQ_query_param_array_amount_with_currency ( pd->price_array_length, pd->price_array, - pg->conn), /* $7 */ - GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $8 */ - GNUNET_PQ_query_param_uint32 (&pd->total_stock_frac), /* $9 */ - GNUNET_PQ_query_param_bool (pd->allow_fractional_quantity), /* $10 */ + pg->conn), /* $8 */ + GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $9 */ + GNUNET_PQ_query_param_uint32 (&pd->total_stock_frac), /* $10 */ + GNUNET_PQ_query_param_bool (pd->allow_fractional_quantity), /* $11 */ GNUNET_PQ_query_param_uint32 (&pd->fractional_precision_level), GNUNET_PQ_query_param_uint64 (&pd->total_lost), - TALER_PQ_query_param_json (pd->address), /* $13 */ + TALER_PQ_query_param_json (pd->address), /* $14 */ GNUNET_PQ_query_param_timestamp (&pd->next_restock), GNUNET_PQ_query_param_uint32 (&pd->minimum_age), GNUNET_PQ_query_param_array_uint64 (num_cats, cats, - pg->conn), /* $16 */ - GNUNET_PQ_query_param_string (pd->product_name), /* $17 */ + pg->conn), /* $17 */ + GNUNET_PQ_query_param_string (pd->product_name), /* $18 */ (0 == pd->product_group_id) ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_uint64 (&pd->product_group_id), (0 == pd->money_pot_id) ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_uint64 (&pd->money_pot_id), /* $19 */ + : GNUNET_PQ_query_param_uint64 (&pd->money_pot_id), /* $20 */ GNUNET_PQ_query_param_bool (pd->price_is_net), GNUNET_PQ_query_param_end }; uint64_t ncat; bool cats_found = true; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("no_instance", + no_instance), GNUNET_PQ_result_spec_bool ("no_product", no_product), GNUNET_PQ_result_spec_bool ("lost_reduced", @@ -97,9 +100,7 @@ TALER_MERCHANTDB_update_product (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - *no_instance = false; if ( (pd->total_stock < pd->total_lost + pd->total_sold) || (pd->total_lost < pd->total_lost + pd->total_sold) /* integer overflow */) @@ -107,27 +108,24 @@ TALER_MERCHANTDB_update_product (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_break (0); return GNUNET_DB_STATUS_HARD_ERROR; } - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_product", - "SELECT" - " out_lost_reduced AS lost_reduced" - ",out_sold_reduced AS sold_reduced" - ",out_stocked_reduced AS stocked_reduced" - ",out_no_product AS no_product" - ",out_no_cat AS no_cat" - ",out_no_group AS no_group" - ",out_no_pot AS no_pot" - " FROM merchant_do_update_product" - "($1,$2,$3::TEXT::JSONB,$4,$5,$6::TEXT::JSONB,$7,$8" - ",$9,$10,$11,$12,$13::TEXT::JSONB,$14,$15,$16,$17" - ",$18,$19,$20);"); + PREPARE (pg, + "update_product", + "SELECT" + " out_lost_reduced AS lost_reduced" + ",out_sold_reduced AS sold_reduced" + ",out_stocked_reduced AS stocked_reduced" + ",out_no_product AS no_product" + ",out_no_cat AS no_cat" + ",out_no_instance AS no_instance" + ",out_no_group AS no_group" + ",out_no_pot AS no_pot" + " FROM merchant_do_update_product" + "($1,$2,$3,$4::TEXT::JSONB,$5,$6,$7::TEXT::JSONB,$8,$9" + ",$10,$11,$12,$13,$14::TEXT::JSONB,$15,$16,$17,$18" + ",$19,$20,$21);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "update_product", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/update_product_group.c b/src/backenddb/update_product_group.c @@ -36,6 +36,7 @@ TALER_MERCHANTDB_update_product_group ( bool *conflict) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&product_group_id), GNUNET_PQ_query_param_string (name), GNUNET_PQ_query_param_string (description), @@ -50,23 +51,18 @@ TALER_MERCHANTDB_update_product_group ( GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_product_group", - "SELECT" - " out_conflict AS conflict" - ",out_not_found AS not_found" - " FROM merchant_do_update_product_group" - "($1,$2,$3);"); + PREPARE (pg, + "update_product_group", + "SELECT" + " out_conflict AS conflict" + ",out_not_found AS not_found" + " FROM merchant_do_update_product_group" + "($1,$2,$3,$4);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "update_product_group", params, rs); if (qs <= 0) diff --git a/src/backenddb/update_report.c b/src/backenddb/update_report.c @@ -40,6 +40,7 @@ TALER_MERCHANTDB_update_report (struct TALER_MERCHANTDB_PostgresContext *pg, { struct GNUNET_TIME_Timestamp start; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&report_id), GNUNET_PQ_query_param_string (report_program_section), GNUNET_PQ_query_param_string (report_description), @@ -51,11 +52,7 @@ TALER_MERCHANTDB_update_report (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_timestamp (&start), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); start = GNUNET_TIME_absolute_to_timestamp ( GNUNET_TIME_absolute_add ( @@ -65,20 +62,23 @@ TALER_MERCHANTDB_update_report (struct TALER_MERCHANTDB_PostgresContext *pg, frequency_shift))); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_report", - "UPDATE merchant_reports SET" - " report_program_section=$2" - " ,report_description=$3" - " ,mime_type=$4" - " ,data_source=$5" - " ,target_address=$6" - " ,frequency=$7" - " ,frequency_shift=$8" - " ,next_transmission=$9" - " WHERE report_serial=$1;"); + PREPARE (pg, + "update_report", + "UPDATE merchant_reports SET" + " report_program_section=$3" + " ,report_description=$4" + " ,mime_type=$5" + " ,data_source=$6" + " ,target_address=$7" + " ,frequency=$8" + " ,frequency_shift=$9" + " ,next_transmission=$10" + " WHERE merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND report_serial=$2;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_report", params); } diff --git a/src/backenddb/update_report_status.c b/src/backenddb/update_report_status.c @@ -36,6 +36,7 @@ TALER_MERCHANTDB_update_report_status (struct TALER_MERCHANTDB_PostgresContext * { uint32_t ec = (uint32_t) last_error_code; struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_uint64 (&report_id), GNUNET_PQ_query_param_timestamp (&next_transmission), TALER_EC_NONE == ec @@ -46,21 +47,20 @@ TALER_MERCHANTDB_update_report_status (struct TALER_MERCHANTDB_PostgresContext * : GNUNET_PQ_query_param_string (last_error_detail), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_report_status", - "UPDATE merchant_reports SET" - " next_transmission=$2" - ",last_error_code=$3" - ",last_error_detail=$4" - " WHERE report_serial=$1;"); + PREPARE (pg, + "update_report_status", + "UPDATE merchant_reports SET" + " next_transmission=$3" + ",last_error_code=$4" + ",last_error_detail=$5" + " WHERE merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND report_serial=$2;"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_report_status", params); } diff --git a/src/backenddb/update_template.c b/src/backenddb/update_template.c @@ -33,6 +33,7 @@ TALER_MERCHANTDB_update_template (struct TALER_MERCHANTDB_PostgresContext *pg, const struct TALER_MERCHANTDB_TemplateDetails *td) { 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->otp_id) @@ -45,29 +46,32 @@ TALER_MERCHANTDB_update_template (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_template", - "WITH otp AS (" - " SELECT otp_serial" - " FROM merchant_otp_devices" - " WHERE otp_id=$3)" - "UPDATE merchant_template SET" - " template_description=$2" - ",otp_device_id=" - " COALESCE((SELECT otp_serial" - " FROM otp), NULL)" - ",template_contract=$4::TEXT::JSONB" - ",editable_defaults=$5::TEXT::JSONB" - " WHERE template_id=$1"); + PREPARE (pg, + "update_template", + "WITH mid AS (" + " SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + ",otp AS (" + " SELECT otp_serial" + " FROM merchant_otp_devices" + " JOIN mid USING (merchant_serial)" + " WHERE otp_id=$4)" + "UPDATE merchant_template SET" + " template_description=$3" + ",otp_device_id=" + " COALESCE((SELECT otp_serial" + " FROM otp), NULL)" + ",template_contract=$5::TEXT::JSONB" + ",editable_defaults=$6::TEXT::JSONB" + " WHERE merchant_serial=" + " (SELECT merchant_serial" + " FROM mid)" + " AND template_id=$2"); qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_template", params); GNUNET_PQ_cleanup_query_params_closures (params); return qs; diff --git a/src/backenddb/update_token_family.c b/src/backenddb/update_token_family.c @@ -33,6 +33,7 @@ TALER_MERCHANTDB_update_token_family (struct TALER_MERCHANTDB_PostgresContext *p const struct TALER_MERCHANTDB_TokenFamilyDetails *details) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (token_family_slug), GNUNET_PQ_query_param_string (details->name), GNUNET_PQ_query_param_string (details->description), @@ -44,24 +45,23 @@ TALER_MERCHANTDB_update_token_family (struct TALER_MERCHANTDB_PostgresContext *p GNUNET_PQ_query_param_timestamp (&details->valid_before), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_token_family", - "UPDATE merchant_token_families SET" - " name=$2" - ",description=$3" - ",description_i18n=$4::TEXT::JSONB" - ",extra_data=$5::TEXT::JSONB" - ",valid_after=$6" - ",valid_before=$7" - " WHERE slug=$1"); + PREPARE (pg, + "update_token_family", + "UPDATE merchant_token_families SET" + " name=$3" + ",description=$4" + ",description_i18n=$5::TEXT::JSONB" + ",extra_data=$6::TEXT::JSONB" + ",valid_after=$7" + ",valid_before=$8" + " WHERE merchant_serial=" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)" + " AND slug=$2"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_token_family", params); } diff --git a/src/backenddb/update_transfer_status.c b/src/backenddb/update_transfer_status.c @@ -51,23 +51,19 @@ TALER_MERCHANTDB_update_transfer_status (struct TALER_MERCHANTDB_PostgresContext GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - - GNUNET_assert (NULL != pg->current_merchant_id); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_transfer_status", - "UPDATE merchant_expected_transfers SET" - " last_http_status=$3" - ",last_ec=$4" - ",last_detail=$5" - ",retry_needed=$6" - ",retry_time=$7" - " WHERE wtid=$1" - " AND exchange_url=$2"); + PREPARE (pg, + "update_transfer_status", + "UPDATE merchant_expected_transfers SET" + " last_http_status=$3" + ",last_ec=$4" + ",last_detail=$5" + ",retry_needed=$6" + ",retry_time=$7" + " WHERE wtid=$1" + " AND exchange_url=$2"); return GNUNET_PQ_eval_prepared_non_select ( pg->conn, - stmt, + "update_transfer_status", params); } diff --git a/src/backenddb/update_unit.c b/src/backenddb/update_unit.c @@ -42,6 +42,7 @@ TALER_MERCHANTDB_update_unit (struct TALER_MERCHANTDB_PostgresContext *pg, bool *builtin_conflict) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance_id), GNUNET_PQ_query_param_string (unit_id), (NULL == unit_name_long) ? GNUNET_PQ_query_param_null () @@ -67,6 +68,8 @@ TALER_MERCHANTDB_update_unit (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("out_no_instance", + no_instance), GNUNET_PQ_result_spec_bool ("out_no_unit", no_unit), GNUNET_PQ_result_spec_bool ("out_builtin_conflict", @@ -74,23 +77,18 @@ TALER_MERCHANTDB_update_unit (struct TALER_MERCHANTDB_PostgresContext *pg, GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - *no_instance = false; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_unit", - "SELECT" - " out_no_unit" - " ,out_builtin_conflict" - " FROM merchant_do_update_unit" - "($1,$2,$3,$4,$5,$6,$7,$8);"); + PREPARE (pg, + "update_unit", + "SELECT" + " out_no_instance" + " ,out_no_unit" + " ,out_builtin_conflict" + " FROM merchant_do_update_unit" + "($1,$2,$3,$4,$5,$6,$7,$8,$9);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - stmt, + "update_unit", params, rs); GNUNET_PQ_cleanup_query_params_closures (params); diff --git a/src/backenddb/update_webhook.c b/src/backenddb/update_webhook.c @@ -32,6 +32,7 @@ TALER_MERCHANTDB_update_webhook (struct TALER_MERCHANTDB_PostgresContext *pg, const struct TALER_MERCHANTDB_WebhookDetails *wb) { 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), @@ -44,24 +45,23 @@ TALER_MERCHANTDB_update_webhook (struct TALER_MERCHANTDB_PostgresContext *pg, : GNUNET_PQ_query_param_string (wb->body_template), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance_id, - pg->current_merchant_id)); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_webhook", - "UPDATE merchant_webhook SET" - " event_type=$2" - ",url=$3" - ",http_method=$4" - ",header_template=$5" - ",body_template=$6" - " WHERE webhook_id=$1"); + PREPARE (pg, + "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"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_webhook", params); } diff --git a/src/backenddb/update_wirewatch_progress.c b/src/backenddb/update_wirewatch_progress.c @@ -33,24 +33,24 @@ TALER_MERCHANTDB_update_wirewatch_progress (struct TALER_MERCHANTDB_PostgresCont uint64_t last_serial) { struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (instance), GNUNET_PQ_query_param_string (payto_uri.full_payto), GNUNET_PQ_query_param_uint64 (&last_serial), GNUNET_PQ_query_param_end }; - char stmt[PG_PREP_INSTANCE_NAME_MAX]; - GNUNET_assert (NULL != pg->current_merchant_id); - GNUNET_assert (0 == strcmp (instance, - pg->current_merchant_id)); + PREPARE (pg, + "update_wirewatch_progress", + "UPDATE merchant_accounts" + " SET last_bank_serial=$3" + " WHERE REGEXP_REPLACE(payto_uri,'\\?.*','')" + " =REGEXP_REPLACE(CAST ($2 AS TEXT),'\\?.*','')" + " AND merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); check_connection (pg); - PREPARE_INSTANCE (pg, - stmt, - "update_wirewatch_progress", - "UPDATE merchant_accounts" - " SET last_bank_serial=$2" - " WHERE REGEXP_REPLACE(payto_uri,'\\?.*','')" - " =REGEXP_REPLACE(CAST ($1 AS TEXT),'\\?.*','')"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - stmt, + "update_wirewatch_progress", params); } diff --git a/src/include/merchant-database/set_instance.h b/src/include/merchant-database/set_instance.h @@ -1,51 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2026 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU 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 src/include/merchant-database/set_instance.h - * @brief Select the per-instance schema active on the connection - * @author Christian Grothoff - */ -#ifndef MERCHANT_DATABASE_SET_INSTANCE_H -#define MERCHANT_DATABASE_SET_INSTANCE_H - -#include <taler/taler_util.h> -#include "merchantdb_lib.h" - - -/** - * Resolve @a instance_id to its merchant_serial and route subsequent - * per-instance queries on @a pg to the corresponding - * `merchant_instance_<serial>` schema. Updates the connection's - * search_path to "merchant_instance_<serial>, merchant" and stores - * @a instance_id / serial in the context for use by per-instance - * call sites and the #PREPARE_INSTANCE macro. - * - * Idempotent: a call with an already-active @a instance_id is a no-op. - * - * @param pg database connection - * @param instance_id instance to activate (must be NUL-terminated) - * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the instance was found - * and the search_path was updated; - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no such instance exists; - * a hard or soft error code otherwise - */ -enum GNUNET_DB_QueryStatus -TALER_MERCHANTDB_set_instance ( - struct TALER_MERCHANTDB_PostgresContext *pg, - const char *instance_id); - - -#endif