merchant

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

merchant-0036-copy.sql.fragment (25916B)


      1     -- =================================================================
      2     -- Copy per-instance row data from merchant.* into the new schema.
      3     -- Tables are listed in FK-dependency order so plain INSERT works
      4     -- (no DEFERRABLE games, no WITH ORDINALITY).  $1 is bound to
      5     -- rec.merchant_serial via USING.  The merchant_serial column is
      6     -- dropped from every column list — the schema name is the
      7     -- discriminator.  All target tables use GENERATED BY DEFAULT AS
      8     -- IDENTITY (so we can keep the source serial values), except
      9     -- merchant_login_tokens whose `serial` is GENERATED ALWAYS — that
     10     -- one needs OVERRIDING SYSTEM VALUE.
     11     --
     12     -- Two stat-meta tables (merchant_statistic_bucket_meta,
     13     -- merchant_statistic_interval_meta) have no merchant_serial in the
     14     -- source schema — they are global slug catalogs and are copied
     15     -- verbatim into every per-instance schema.
     16     -- =================================================================
     17 
     18     -- ---------------- direct merchant_serial (no JOIN) --------------
     19 
     20     EXECUTE FORMAT('INSERT INTO %I.merchant_accounts'
     21       || ' (account_serial, h_wire, salt, credit_facade_url,'
     22       || '  credit_facade_credentials, last_bank_serial, payto_uri,'
     23       || '  active, extra_wire_subject_metadata)'
     24       || ' SELECT account_serial, h_wire, salt, credit_facade_url,'
     25       || '        credit_facade_credentials, last_bank_serial, payto_uri,'
     26       || '        active, extra_wire_subject_metadata'
     27       || '   FROM merchant.merchant_accounts'
     28       || '  WHERE merchant_serial = $1', s)
     29       USING rec.merchant_serial;
     30 
     31     EXECUTE FORMAT('INSERT INTO %I.merchant_categories'
     32       || ' (category_serial, category_name, category_name_i18n)'
     33       || ' SELECT category_serial, category_name, category_name_i18n'
     34       || '   FROM merchant.merchant_categories'
     35       || '  WHERE merchant_serial = $1', s)
     36       USING rec.merchant_serial;
     37 
     38     EXECUTE FORMAT('INSERT INTO %I.merchant_contract_terms'
     39       || ' (order_serial, order_id, contract_terms, wallet_data,'
     40       || '  h_contract_terms, creation_time, pay_deadline, refund_deadline,'
     41       || '  paid, wired, fulfillment_url, session_id, pos_key, pos_algorithm,'
     42       || '  claim_token, choice_index)'
     43       || ' SELECT order_serial, order_id, contract_terms, wallet_data,'
     44       || '        h_contract_terms, creation_time, pay_deadline, refund_deadline,'
     45       || '        paid, wired, fulfillment_url, session_id, pos_key, pos_algorithm,'
     46       || '        claim_token, choice_index'
     47       || '   FROM merchant.merchant_contract_terms'
     48       || '  WHERE merchant_serial = $1', s)
     49       USING rec.merchant_serial;
     50 
     51     EXECUTE FORMAT('INSERT INTO %I.merchant_custom_units'
     52       || ' (unit_serial, unit, unit_name_long, unit_name_short,'
     53       || '  unit_name_long_i18n, unit_name_short_i18n,'
     54       || '  unit_allow_fraction, unit_precision_level, unit_active)'
     55       || ' SELECT unit_serial, unit, unit_name_long, unit_name_short,'
     56       || '        unit_name_long_i18n, unit_name_short_i18n,'
     57       || '        unit_allow_fraction, unit_precision_level, unit_active'
     58       || '   FROM merchant.merchant_custom_units'
     59       || '  WHERE merchant_serial = $1', s)
     60       USING rec.merchant_serial;
     61 
     62     -- merchant_donau_instances: source filter column is named
     63     -- merchant_instance_serial (not merchant_serial).
     64     EXECUTE FORMAT('INSERT INTO %I.merchant_donau_instances'
     65       || ' (donau_instances_serial, donau_url, charity_name, charity_id,'
     66       || '  charity_max_per_year, charity_receipts_to_date, current_year)'
     67       || ' SELECT donau_instances_serial, donau_url, charity_name, charity_id,'
     68       || '        charity_max_per_year, charity_receipts_to_date, current_year'
     69       || '   FROM merchant.merchant_donau_instances'
     70       || '  WHERE merchant_instance_serial = $1', s)
     71       USING rec.merchant_serial;
     72 
     73     -- merchant_login_tokens: target `serial` column is GENERATED ALWAYS
     74     -- → must use OVERRIDING SYSTEM VALUE to preserve serial values.
     75     EXECUTE FORMAT('INSERT INTO %I.merchant_login_tokens'
     76       || ' (token, creation_time, expiration_time, validity_scope,'
     77       || '  description, serial)'
     78       || ' OVERRIDING SYSTEM VALUE'
     79       || ' SELECT token, creation_time, expiration_time, validity_scope,'
     80       || '        description, serial'
     81       || '   FROM merchant.merchant_login_tokens'
     82       || '  WHERE merchant_serial = $1', s)
     83       USING rec.merchant_serial;
     84 
     85     EXECUTE FORMAT('INSERT INTO %I.merchant_money_pots'
     86       || ' (money_pot_serial, money_pot_name, money_pot_description, pot_totals)'
     87       || ' SELECT money_pot_serial, money_pot_name, money_pot_description, pot_totals'
     88       || '   FROM merchant.merchant_money_pots'
     89       || '  WHERE merchant_serial = $1', s)
     90       USING rec.merchant_serial;
     91 
     92     EXECUTE FORMAT('INSERT INTO %I.merchant_otp_devices'
     93       || ' (otp_serial, otp_id, otp_description, otp_key, otp_algorithm, otp_ctr)'
     94       || ' SELECT otp_serial, otp_id, otp_description, otp_key, otp_algorithm, otp_ctr'
     95       || '   FROM merchant.merchant_otp_devices'
     96       || '  WHERE merchant_serial = $1', s)
     97       USING rec.merchant_serial;
     98 
     99     EXECUTE FORMAT('INSERT INTO %I.merchant_orders'
    100       || ' (order_serial, order_id, claim_token, h_post_data, pay_deadline,'
    101       || '  creation_time, contract_terms, pos_key, pos_algorithm,'
    102       || '  fulfillment_url, session_id)'
    103       || ' SELECT order_serial, order_id, claim_token, h_post_data, pay_deadline,'
    104       || '        creation_time, contract_terms, pos_key, pos_algorithm,'
    105       || '        fulfillment_url, session_id'
    106       || '   FROM merchant.merchant_orders'
    107       || '  WHERE merchant_serial = $1', s)
    108       USING rec.merchant_serial;
    109 
    110     EXECUTE FORMAT('INSERT INTO %I.merchant_product_groups'
    111       || ' (product_group_serial, product_group_name, product_group_description)'
    112       || ' SELECT product_group_serial, product_group_name, product_group_description'
    113       || '   FROM merchant.merchant_product_groups'
    114       || '  WHERE merchant_serial = $1', s)
    115       USING rec.merchant_serial;
    116 
    117     -- merchant_inventory references merchant_product_groups and merchant_money_pots
    118     -- (already copied above).
    119     EXECUTE FORMAT('INSERT INTO %I.merchant_inventory'
    120       || ' (product_serial, product_id, description, description_i18n,'
    121       || '  unit, image, taxes, total_stock, total_sold, total_lost,'
    122       || '  address, next_restock, minimum_age, product_name, image_hash,'
    123       || '  price_array, total_stock_frac, total_sold_frac, total_lost_frac,'
    124       || '  allow_fractional_quantity, fractional_precision_level,'
    125       || '  product_group_serial, money_pot_serial, price_is_net)'
    126       || ' SELECT product_serial, product_id, description, description_i18n,'
    127       || '        unit, image, taxes, total_stock, total_sold, total_lost,'
    128       || '        address, next_restock, minimum_age, product_name, image_hash,'
    129       || '        price_array, total_stock_frac, total_sold_frac, total_lost_frac,'
    130       || '        allow_fractional_quantity, fractional_precision_level,'
    131       || '        product_group_serial, money_pot_serial, price_is_net'
    132       || '   FROM merchant.merchant_inventory'
    133       || '  WHERE merchant_serial = $1', s)
    134       USING rec.merchant_serial;
    135 
    136     EXECUTE FORMAT('INSERT INTO %I.merchant_reports'
    137       || ' (report_serial, report_program_section, report_description,'
    138       || '  mime_type, report_token, data_source, target_address,'
    139       || '  frequency, frequency_shift, next_transmission,'
    140       || '  last_error_code, last_error_detail, one_shot_hidden)'
    141       || ' SELECT report_serial, report_program_section, report_description,'
    142       || '        mime_type, report_token, data_source, target_address,'
    143       || '        frequency, frequency_shift, next_transmission,'
    144       || '        last_error_code, last_error_detail, one_shot_hidden'
    145       || '   FROM merchant.merchant_reports'
    146       || '  WHERE merchant_serial = $1', s)
    147       USING rec.merchant_serial;
    148 
    149     -- merchant_template references merchant_otp_devices (already copied).
    150     EXECUTE FORMAT('INSERT INTO %I.merchant_template'
    151       || ' (template_serial, template_id, template_description,'
    152       || '  otp_device_id, template_contract, editable_defaults)'
    153       || ' SELECT template_serial, template_id, template_description,'
    154       || '        otp_device_id, template_contract, editable_defaults'
    155       || '   FROM merchant.merchant_template'
    156       || '  WHERE merchant_serial = $1', s)
    157       USING rec.merchant_serial;
    158 
    159     EXECUTE FORMAT('INSERT INTO %I.merchant_token_families'
    160       || ' (token_family_serial, slug, name, description, description_i18n,'
    161       || '  valid_after, valid_before, duration, kind, issued, used,'
    162       || '  validity_granularity, start_offset, cipher_choice, extra_data)'
    163       || ' SELECT token_family_serial, slug, name, description, description_i18n,'
    164       || '        valid_after, valid_before, duration, kind, issued, used,'
    165       || '        validity_granularity, start_offset, cipher_choice, extra_data'
    166       || '   FROM merchant.merchant_token_families'
    167       || '  WHERE merchant_serial = $1', s)
    168       USING rec.merchant_serial;
    169 
    170     EXECUTE FORMAT('INSERT INTO %I.merchant_webhook'
    171       || ' (webhook_serial, webhook_id, event_type, url, http_method,'
    172       || '  header_template, body_template)'
    173       || ' SELECT webhook_serial, webhook_id, event_type, url, http_method,'
    174       || '        header_template, body_template'
    175       || '   FROM merchant.merchant_webhook'
    176       || '  WHERE merchant_serial = $1', s)
    177       USING rec.merchant_serial;
    178 
    179 
    180     EXECUTE FORMAT('INSERT INTO %I.merchant_builtin_unit_overrides'
    181       || ' (builtin_unit_serial, override_allow_fraction,'
    182       || '  override_precision_level, override_active)'
    183       || ' SELECT builtin_unit_serial, override_allow_fraction,'
    184       || '        override_precision_level, override_active'
    185       || '   FROM merchant.merchant_builtin_unit_overrides'
    186       || '  WHERE merchant_serial = $1', s)
    187       USING rec.merchant_serial;
    188 
    189     -- ---------------- statistics: meta tables are GLOBAL ------------
    190     -- Copied unfiltered (every instance gets a full stats catalog).
    191 
    192     EXECUTE FORMAT('INSERT INTO %I.merchant_statistic_bucket_meta'
    193       || ' (bmeta_serial_id, slug, description, stype, ranges, ages)'
    194       || ' SELECT bmeta_serial_id, slug, description, stype, ranges, ages'
    195       || '   FROM merchant.merchant_statistic_bucket_meta', s);
    196 
    197     EXECUTE FORMAT('INSERT INTO %I.merchant_statistic_interval_meta'
    198       || ' (imeta_serial_id, slug, description, stype, ranges, precisions)'
    199       || ' SELECT imeta_serial_id, slug, description, stype, ranges, precisions'
    200       || '   FROM merchant.merchant_statistic_interval_meta', s);
    201 
    202     -- ---------------- statistics: per-instance event/bucket tables --
    203 
    204     EXECUTE FORMAT('INSERT INTO %I.merchant_statistic_amount_event'
    205       || ' (aevent_serial_id, imeta_serial_id, slot,'
    206       || '  delta_curr, delta_value, delta_frac)'
    207       || ' SELECT aevent_serial_id, imeta_serial_id, slot,'
    208       || '        delta_curr, delta_value, delta_frac'
    209       || '   FROM merchant.merchant_statistic_amount_event'
    210       || '  WHERE merchant_serial = $1', s)
    211       USING rec.merchant_serial;
    212 
    213     EXECUTE FORMAT('INSERT INTO %I.merchant_statistic_counter_event'
    214       || ' (nevent_serial_id, imeta_serial_id, slot, delta)'
    215       || ' SELECT nevent_serial_id, imeta_serial_id, slot, delta'
    216       || '   FROM merchant.merchant_statistic_counter_event'
    217       || '  WHERE merchant_serial = $1', s)
    218       USING rec.merchant_serial;
    219 
    220     EXECUTE FORMAT('INSERT INTO %I.merchant_statistic_bucket_amount'
    221       || ' (bmeta_serial_id, bucket_start, bucket_range, curr,'
    222       || '  cumulative_value, cumulative_frac)'
    223       || ' SELECT bmeta_serial_id, bucket_start, bucket_range, curr,'
    224       || '        cumulative_value, cumulative_frac'
    225       || '   FROM merchant.merchant_statistic_bucket_amount'
    226       || '  WHERE merchant_serial = $1', s)
    227       USING rec.merchant_serial;
    228 
    229     EXECUTE FORMAT('INSERT INTO %I.merchant_statistic_bucket_counter'
    230       || ' (bmeta_serial_id, bucket_start, bucket_range, cumulative_number)'
    231       || ' SELECT bmeta_serial_id, bucket_start, bucket_range, cumulative_number'
    232       || '   FROM merchant.merchant_statistic_bucket_counter'
    233       || '  WHERE merchant_serial = $1', s)
    234       USING rec.merchant_serial;
    235 
    236     EXECUTE FORMAT('INSERT INTO %I.merchant_statistic_interval_amount'
    237       || ' (imeta_serial_id, event_delimiter, range, curr,'
    238       || '  cumulative_value, cumulative_frac)'
    239       || ' SELECT imeta_serial_id, event_delimiter, range, curr,'
    240       || '        cumulative_value, cumulative_frac'
    241       || '   FROM merchant.merchant_statistic_interval_amount'
    242       || '  WHERE merchant_serial = $1', s)
    243       USING rec.merchant_serial;
    244 
    245     EXECUTE FORMAT('INSERT INTO %I.merchant_statistic_interval_counter'
    246       || ' (imeta_serial_id, range, event_delimiter, cumulative_number)'
    247       || ' SELECT imeta_serial_id, range, event_delimiter, cumulative_number'
    248       || '   FROM merchant.merchant_statistic_interval_counter'
    249       || '  WHERE merchant_serial = $1', s)
    250       USING rec.merchant_serial;
    251 
    252     -- ---------------- account-linked (JOIN via account_serial) ------
    253 
    254     EXECUTE FORMAT('INSERT INTO %I.merchant_kyc'
    255       || ' (kyc_serial_id, kyc_timestamp, kyc_ok, account_serial,'
    256       || '  exchange_url, access_token, exchange_http_status,'
    257       || '  exchange_ec_code, aml_review, jaccount_limits,'
    258       || '  last_rule_gen, next_kyc_poll, kyc_backoff)'
    259       || ' SELECT k.kyc_serial_id, k.kyc_timestamp, k.kyc_ok, k.account_serial,'
    260       || '        k.exchange_url, k.access_token, k.exchange_http_status,'
    261       || '        k.exchange_ec_code, k.aml_review, k.jaccount_limits,'
    262       || '        k.last_rule_gen, k.next_kyc_poll, k.kyc_backoff'
    263       || '   FROM merchant.merchant_kyc k'
    264       || '   JOIN merchant.merchant_accounts a'
    265       || '     ON k.account_serial = a.account_serial'
    266       || '  WHERE a.merchant_serial = $1', s)
    267       USING rec.merchant_serial;
    268 
    269     EXECUTE FORMAT('INSERT INTO %I.merchant_deposit_confirmations'
    270       || ' (deposit_confirmation_serial, order_serial, deposit_timestamp,'
    271       || '  exchange_url, total_without_fee, wire_fee, signkey_serial,'
    272       || '  exchange_sig, account_serial, wire_transfer_deadline,'
    273       || '  wire_pending, exchange_failure, retry_backoff)'
    274       || ' SELECT dc.deposit_confirmation_serial, dc.order_serial, dc.deposit_timestamp,'
    275       || '        dc.exchange_url, dc.total_without_fee, dc.wire_fee, dc.signkey_serial,'
    276       || '        dc.exchange_sig, dc.account_serial, dc.wire_transfer_deadline,'
    277       || '        dc.wire_pending, dc.exchange_failure, dc.retry_backoff'
    278       || '   FROM merchant.merchant_deposit_confirmations dc'
    279       || '   JOIN merchant.merchant_accounts a'
    280       || '     ON dc.account_serial = a.account_serial'
    281       || '  WHERE a.merchant_serial = $1', s)
    282       USING rec.merchant_serial;
    283 
    284     EXECUTE FORMAT('INSERT INTO %I.merchant_expected_transfers'
    285       || ' (expected_credit_serial, exchange_url, wtid, expected_credit_amount,'
    286       || '  wire_fee, account_serial, expected_time, retry_time, last_http_status,'
    287       || '  last_ec, last_detail, retry_needed, signkey_serial, exchange_sig,'
    288       || '  h_details, confirmed)'
    289       || ' SELECT et.expected_credit_serial, et.exchange_url, et.wtid, et.expected_credit_amount,'
    290       || '        et.wire_fee, et.account_serial, et.expected_time, et.retry_time, et.last_http_status,'
    291       || '        et.last_ec, et.last_detail, et.retry_needed, et.signkey_serial, et.exchange_sig,'
    292       || '        et.h_details, et.confirmed'
    293       || '   FROM merchant.merchant_expected_transfers et'
    294       || '   JOIN merchant.merchant_accounts a'
    295       || '     ON et.account_serial = a.account_serial'
    296       || '  WHERE a.merchant_serial = $1', s)
    297       USING rec.merchant_serial;
    298 
    299     EXECUTE FORMAT('INSERT INTO %I.merchant_transfers'
    300       || ' (credit_serial, exchange_url, wtid, credit_amount, account_serial,'
    301       || '  bank_serial_id, expected, execution_time)'
    302       || ' SELECT t.credit_serial, t.exchange_url, t.wtid, t.credit_amount, t.account_serial,'
    303       || '        t.bank_serial_id, t.expected, t.execution_time'
    304       || '   FROM merchant.merchant_transfers t'
    305       || '   JOIN merchant.merchant_accounts a'
    306       || '     ON t.account_serial = a.account_serial'
    307       || '  WHERE a.merchant_serial = $1', s)
    308       USING rec.merchant_serial;
    309 
    310     -- ---------------- deposits: deeper join (dc → accounts) ---------
    311 
    312     EXECUTE FORMAT('INSERT INTO %I.merchant_deposits'
    313       || ' (deposit_serial, coin_offset, deposit_confirmation_serial,'
    314       || '  coin_pub, coin_sig, amount_with_fee, deposit_fee, refund_fee,'
    315       || '  settlement_retry_needed, settlement_retry_time,'
    316       || '  settlement_last_http_status, settlement_last_ec,'
    317       || '  settlement_last_detail, settlement_wtid,'
    318       || '  settlement_coin_contribution, settlement_expected_credit_serial,'
    319       || '  signkey_serial, settlement_exchange_sig)'
    320       || ' SELECT d.deposit_serial, d.coin_offset, d.deposit_confirmation_serial,'
    321       || '        d.coin_pub, d.coin_sig, d.amount_with_fee, d.deposit_fee, d.refund_fee,'
    322       || '        d.settlement_retry_needed, d.settlement_retry_time,'
    323       || '        d.settlement_last_http_status, d.settlement_last_ec,'
    324       || '        d.settlement_last_detail, d.settlement_wtid,'
    325       || '        d.settlement_coin_contribution, d.settlement_expected_credit_serial,'
    326       || '        d.signkey_serial, d.settlement_exchange_sig'
    327       || '   FROM merchant.merchant_deposits d'
    328       || '   JOIN merchant.merchant_deposit_confirmations dc'
    329       || '     ON d.deposit_confirmation_serial = dc.deposit_confirmation_serial'
    330       || '   JOIN merchant.merchant_accounts a'
    331       || '     ON dc.account_serial = a.account_serial'
    332       || '  WHERE a.merchant_serial = $1', s)
    333       USING rec.merchant_serial;
    334 
    335     -- expected_transfer_to_coin: deposit_serial → deposits → dc → accounts.
    336     EXECUTE FORMAT('INSERT INTO %I.merchant_expected_transfer_to_coin'
    337       || ' (deposit_serial, expected_credit_serial, offset_in_exchange_list,'
    338       || '  exchange_deposit_value, exchange_deposit_fee)'
    339       || ' SELECT ettc.deposit_serial, ettc.expected_credit_serial, ettc.offset_in_exchange_list,'
    340       || '        ettc.exchange_deposit_value, ettc.exchange_deposit_fee'
    341       || '   FROM merchant.merchant_expected_transfer_to_coin ettc'
    342       || '   JOIN merchant.merchant_deposits d'
    343       || '     ON ettc.deposit_serial = d.deposit_serial'
    344       || '   JOIN merchant.merchant_deposit_confirmations dc'
    345       || '     ON d.deposit_confirmation_serial = dc.deposit_confirmation_serial'
    346       || '   JOIN merchant.merchant_accounts a'
    347       || '     ON dc.account_serial = a.account_serial'
    348       || '  WHERE a.merchant_serial = $1', s)
    349       USING rec.merchant_serial;
    350 
    351     -- transfer_signatures: expected_credit_serial → expected_transfers → accounts.
    352     EXECUTE FORMAT('INSERT INTO %I.merchant_transfer_signatures'
    353       || ' (expected_credit_serial, signkey_serial, wire_fee, credit_amount,'
    354       || '  execution_time, exchange_sig)'
    355       || ' SELECT ts.expected_credit_serial, ts.signkey_serial, ts.wire_fee, ts.credit_amount,'
    356       || '        ts.execution_time, ts.exchange_sig'
    357       || '   FROM merchant.merchant_transfer_signatures ts'
    358       || '   JOIN merchant.merchant_expected_transfers et'
    359       || '     ON ts.expected_credit_serial = et.expected_credit_serial'
    360       || '   JOIN merchant.merchant_accounts a'
    361       || '     ON et.account_serial = a.account_serial'
    362       || '  WHERE a.merchant_serial = $1', s)
    363       USING rec.merchant_serial;
    364 
    365     -- ---------------- order/contract-linked (JOIN via order_serial) ---
    366 
    367     EXECUTE FORMAT('INSERT INTO %I.merchant_refunds'
    368       || ' (refund_serial, order_serial, rtransaction_id, refund_timestamp,'
    369       || '  coin_pub, reason, refund_amount)'
    370       || ' SELECT r.refund_serial, r.order_serial, r.rtransaction_id, r.refund_timestamp,'
    371       || '        r.coin_pub, r.reason, r.refund_amount'
    372       || '   FROM merchant.merchant_refunds r'
    373       || '   JOIN merchant.merchant_contract_terms ct'
    374       || '     ON r.order_serial = ct.order_serial'
    375       || '  WHERE ct.merchant_serial = $1', s)
    376       USING rec.merchant_serial;
    377 
    378     EXECUTE FORMAT('INSERT INTO %I.merchant_refund_proofs'
    379       || ' (refund_serial, exchange_sig, signkey_serial)'
    380       || ' SELECT rp.refund_serial, rp.exchange_sig, rp.signkey_serial'
    381       || '   FROM merchant.merchant_refund_proofs rp'
    382       || '   JOIN merchant.merchant_refunds r'
    383       || '     ON rp.refund_serial = r.refund_serial'
    384       || '   JOIN merchant.merchant_contract_terms ct'
    385       || '     ON r.order_serial = ct.order_serial'
    386       || '  WHERE ct.merchant_serial = $1', s)
    387       USING rec.merchant_serial;
    388 
    389     EXECUTE FORMAT('INSERT INTO %I.merchant_order_token_blinded_sigs'
    390       || ' (order_token_bs_serial, order_serial, token_index,'
    391       || '  token_blinded_signature, token_hash)'
    392       || ' SELECT otbs.order_token_bs_serial, otbs.order_serial, otbs.token_index,'
    393       || '        otbs.token_blinded_signature, otbs.token_hash'
    394       || '   FROM merchant.merchant_order_token_blinded_sigs otbs'
    395       || '   JOIN merchant.merchant_contract_terms ct'
    396       || '     ON otbs.order_serial = ct.order_serial'
    397       || '  WHERE ct.merchant_serial = $1', s)
    398       USING rec.merchant_serial;
    399 
    400     -- unclaim_signatures: linked via h_contract_terms.
    401     EXECUTE FORMAT('INSERT INTO %I.merchant_unclaim_signatures'
    402       || ' (unclaim_serial, h_contract_terms, unclaim_sig, expiration_time)'
    403       || ' SELECT us.unclaim_serial, us.h_contract_terms, us.unclaim_sig, us.expiration_time'
    404       || '   FROM merchant.merchant_unclaim_signatures us'
    405       || '   JOIN merchant.merchant_contract_terms ct'
    406       || '     ON us.h_contract_terms = ct.h_contract_terms'
    407       || '  WHERE ct.merchant_serial = $1', s)
    408       USING rec.merchant_serial;
    409 
    410     -- order_locks: order_serial → contract_terms.
    411     EXECUTE FORMAT('INSERT INTO %I.merchant_order_locks'
    412       || ' (product_serial, total_locked, order_serial, total_locked_frac)'
    413       || ' SELECT ol.product_serial, ol.total_locked, ol.order_serial, ol.total_locked_frac'
    414       || '   FROM merchant.merchant_order_locks ol'
    415       || '   JOIN merchant.merchant_contract_terms ct'
    416       || '     ON ol.order_serial = ct.order_serial'
    417       || '  WHERE ct.merchant_serial = $1', s)
    418       USING rec.merchant_serial;
    419 
    420     -- inventory_locks: product_serial → inventory.
    421     EXECUTE FORMAT('INSERT INTO %I.merchant_inventory_locks'
    422       || ' (product_serial, lock_uuid, total_locked, expiration, total_locked_frac)'
    423       || ' SELECT il.product_serial, il.lock_uuid, il.total_locked, il.expiration, il.total_locked_frac'
    424       || '   FROM merchant.merchant_inventory_locks il'
    425       || '   JOIN merchant.merchant_inventory i'
    426       || '     ON il.product_serial = i.product_serial'
    427       || '  WHERE i.merchant_serial = $1', s)
    428       USING rec.merchant_serial;
    429 
    430     -- product_categories: junction; pivot on product_serial → inventory.
    431     EXECUTE FORMAT('INSERT INTO %I.merchant_product_categories'
    432       || ' (category_serial, product_serial)'
    433       || ' SELECT pc.category_serial, pc.product_serial'
    434       || '   FROM merchant.merchant_product_categories pc'
    435       || '   JOIN merchant.merchant_inventory i'
    436       || '     ON pc.product_serial = i.product_serial'
    437       || '  WHERE i.merchant_serial = $1', s)
    438       USING rec.merchant_serial;
    439 
    440     -- ---------------- token chain (token_families → keys → tokens) --
    441 
    442     EXECUTE FORMAT('INSERT INTO %I.merchant_token_family_keys'
    443       || ' (token_family_key_serial, token_family_serial, pub, h_pub,'
    444       || '  priv, cipher, signature_validity_start, signature_validity_end,'
    445       || '  private_key_deleted_at, private_key_created_at)'
    446       || ' SELECT tfk.token_family_key_serial, tfk.token_family_serial, tfk.pub, tfk.h_pub,'
    447       || '        tfk.priv, tfk.cipher, tfk.signature_validity_start, tfk.signature_validity_end,'
    448       || '        tfk.private_key_deleted_at, tfk.private_key_created_at'
    449       || '   FROM merchant.merchant_token_family_keys tfk'
    450       || '   JOIN merchant.merchant_token_families tf'
    451       || '     ON tfk.token_family_serial = tf.token_family_serial'
    452       || '  WHERE tf.merchant_serial = $1', s)
    453       USING rec.merchant_serial;
    454 
    455     EXECUTE FORMAT('INSERT INTO %I.merchant_issued_tokens'
    456       || ' (issued_token_serial, h_contract_terms, token_family_key_serial, blind_sig)'
    457       || ' SELECT it.issued_token_serial, it.h_contract_terms, it.token_family_key_serial, it.blind_sig'
    458       || '   FROM merchant.merchant_issued_tokens it'
    459       || '   JOIN merchant.merchant_token_family_keys tfk'
    460       || '     ON it.token_family_key_serial = tfk.token_family_key_serial'
    461       || '   JOIN merchant.merchant_token_families tf'
    462       || '     ON tfk.token_family_serial = tf.token_family_serial'
    463       || '  WHERE tf.merchant_serial = $1', s)
    464       USING rec.merchant_serial;
    465 
    466     EXECUTE FORMAT('INSERT INTO %I.merchant_used_tokens'
    467       || ' (spent_token_serial, h_contract_terms, token_family_key_serial,'
    468       || '  token_pub, token_sig, blind_sig)'
    469       || ' SELECT ut.spent_token_serial, ut.h_contract_terms, ut.token_family_key_serial,'
    470       || '        ut.token_pub, ut.token_sig, ut.blind_sig'
    471       || '   FROM merchant.merchant_used_tokens ut'
    472       || '   JOIN merchant.merchant_token_family_keys tfk'
    473       || '     ON ut.token_family_key_serial = tfk.token_family_key_serial'
    474       || '   JOIN merchant.merchant_token_families tf'
    475       || '     ON tfk.token_family_serial = tf.token_family_serial'
    476       || '  WHERE tf.merchant_serial = $1', s)
    477       USING rec.merchant_serial;