gendesign/tradein-mvp/backend/data/sql/085_quarter_price_index_fdw.sql
bot-backend 76baf8011d feat(db): quarter_price_index FDW foreign table + monthly refresh (Refs #762)
#647-2 (P0, инфра — клиентскую цену НЕ меняет). Делает mv_quarter_price_index
(#760) читаемым estimator'ом за O(1) + периодический refresh.

- tradein 085_quarter_price_index_fdw.sql: FOREIGN TABLE quarter_price_index
  над main public.mv_quarter_price_index через существующий server
  gendesign_remote (паттерн 060_postgres_fdw_extension.sql, per-table). Колонки
  1:1 с MV (varchar(30)/float8/bigint/text/timestamptz). DROP IF EXISTS идемпот.
- main 99b_grant_quarter_price_index_fdw.sql: GRANT SELECT на MV роли
  tradein_fdw_reader (иначе foreign table падает; зеркало 122_grant_rosreestr).
- main refresh: Celery beat (pg_cron нет). quarter_price_index_refresh.py —
  chained REFRESH mv_quarter_price_per_m2 → mv_quarter_price_index CONCURRENTLY;
  task refresh_quarter_price_index.py; beat-entry 'refresh-quarter-price-index'
  cron '0 2 5 * *' (05:00 MSK 5-го, после ekb-districts-medians).

Lookup-индекс: MV уже несёт UNIQUE mv_quarter_price_index_uq (O(1) remote);
foreign table не нуждается в локальном. Валидация (postgres-gendesign): MV
1972 строки, 0 NULL, UNIQUE indisunique=true, REFRESH CONCURRENTLY валиден.

Разблокирует #647-3 (estimator integration, за гейтом #763).
2026-05-30 20:13:02 +03:00

58 lines
2.7 KiB
PL/PgSQL

-- 085_quarter_price_index_fdw.sql
-- Issue #762 — Foreign table over MAIN DB's mv_quarter_price_index MV.
--
-- Context:
-- #760 (PR #792) created public.mv_quarter_price_index in the MAIN gendesign DB.
-- The estimator (#647-3) needs O(1) lookup by quarter_cad_number without a
-- cross-service HTTP round-trip. FDW foreign table lets tradein read the MV
-- directly via the already-established gendesign_remote server.
--
-- Reused infra:
-- SERVER gendesign_remote — created by 060_postgres_fdw_extension.sql.
-- Pattern: per-table FOREIGN TABLE (not IMPORT FOREIGN SCHEMA) — matches repo convention.
-- USER MAPPING is managed at backend startup from env (see 060_postgres_fdw_extension.sql).
--
-- Column mapping (verified against live MV via pg_attribute, 2026-05-30):
-- quarter_cad_number character varying(30) → varchar(30) / text (FDW accepts text for varchar)
-- price_index double precision → double precision
-- n_deals bigint → bigint
-- basis text → text
-- computed_at timestamp with time zone → timestamptz
--
-- Index note:
-- The remote MV has UNIQUE index mv_quarter_price_index_uq on quarter_cad_number.
-- FDW pushes equality predicates (WHERE quarter_cad_number = $1) down to MAIN,
-- so remote index gives O(1) lookup. No local index on the foreign table is needed
-- or possible (postgres_fdw does not support local indexes on foreign tables).
--
-- Idempotency:
-- DROP FOREIGN TABLE IF EXISTS → CREATE is safe to re-apply.
-- BEGIN/COMMIT per sql.md conventions.
--
-- Apply order:
-- After 060_postgres_fdw_extension.sql (server must exist).
-- After 99a_quarter_price_index.sql is applied in MAIN (MV must exist remotely).
-- Before estimator code referencing this table is deployed (#647-3).
BEGIN;
DROP FOREIGN TABLE IF EXISTS quarter_price_index;
CREATE FOREIGN TABLE quarter_price_index (
quarter_cad_number varchar(30) NOT NULL,
price_index double precision NOT NULL,
n_deals bigint NOT NULL,
basis text NOT NULL,
computed_at timestamp with time zone NOT NULL
)
SERVER gendesign_remote
OPTIONS (schema_name 'public', table_name 'mv_quarter_price_index');
COMMENT ON FOREIGN TABLE quarter_price_index IS
'FDW read-only view of gendesign.public.mv_quarter_price_index. '
'Per-cadastral-quarter price index normalised to EKB city median (=1.0). '
'Lookup by quarter_cad_number is O(1) via remote UNIQUE index on MAIN. '
'Refresh cadence: MAIN refreshes MV via Celery beat (see quarter_price_index_refresh task). '
'Issue: #762. Source MV: #760.';
COMMIT;