diff --git a/data/sql/92_cad_bulk_layers.sql b/data/sql/92_cad_bulk_layers.sql index a3a0fefd..cb5a8b83 100644 --- a/data/sql/92_cad_bulk_layers.sql +++ b/data/sql/92_cad_bulk_layers.sql @@ -32,7 +32,9 @@ -- J. Migrate data: cad_parcels_geom -> cad_parcels -- K. Backup + replace: cad_buildings -> cad_buildings_old_apr26 -> DROP + rename -- L. Restore FK + views --- M. DROP cad_parcels_geom (after data migrated) +-- M. Replace cad_parcels_geom TABLE with back-compat VIEW over cad_parcels +-- N. COMMENT ON TABLE for all 8 tables +-- O. set_updated_at triggers for all tables with updated_at column -- -- Idempotency: safe to re-run — all DDL uses IF NOT EXISTS / IF EXISTS guards. -- Data migrations use INSERT ... ON CONFLICT DO NOTHING. @@ -293,7 +295,11 @@ CREATE TABLE IF NOT EXISTS cadastre_jobs ( error TEXT, phase_state JSONB, raw_targets JSONB, - CONSTRAINT cadastre_jobs_pkey PRIMARY KEY (job_id) + CONSTRAINT cadastre_jobs_pkey PRIMARY KEY (job_id), + CONSTRAINT cadastre_jobs_status_chk + CHECK (status IN ( + 'queued', 'running', 'done', 'failed', 'zombie', 'cancelled', 'paused' + )) ); -- =========================================================================== @@ -499,14 +505,13 @@ BEGIN ALTER TABLE cad_buildings_new RENAME TO cad_buildings; -- Rename indexes to drop the _new suffix ALTER INDEX IF EXISTS cad_buildings_new_pkey RENAME TO cad_buildings_pkey; + -- Note: ALTER INDEX RENAME on a PK index also renames the constraint + -- automatically in PG 16. No separate RENAME CONSTRAINT needed. ALTER INDEX IF EXISTS cad_buildings_new_geom_gist RENAME TO cad_buildings_geom_gist; ALTER INDEX IF EXISTS cad_buildings_new_quarter_idx RENAME TO cad_buildings_quarter_idx; ALTER INDEX IF EXISTS cad_buildings_new_objdoc_idx RENAME TO cad_buildings_objdoc_idx; ALTER INDEX IF EXISTS cad_buildings_new_complex_idx RENAME TO cad_buildings_complex_idx; ALTER INDEX IF EXISTS cad_buildings_new_purpose_idx RENAME TO cad_buildings_purpose_idx; - -- Rename constraint - ALTER TABLE cad_buildings - RENAME CONSTRAINT cad_buildings_new_pkey TO cad_buildings_pkey; END IF; END $$; @@ -627,21 +632,159 @@ SELECT FROM complexes c; -- =========================================================================== --- M. DROP cad_parcels_geom (original table — data migrated to cad_parcels) --- Only dropped if cad_parcels exists and has at least as many rows. --- Guard: skip drop if the new table doesn't exist yet (paranoia). +-- M. Replace cad_parcels_geom TABLE with a back-compat VIEW over cad_parcels. +-- Original table had exactly: cad_num, geom, raw_props, fetched_at. +-- The view preserves the same 4-column signature so that any legacy callers +-- (e.g. site-finder analyze fallback) continue to work without code changes. +-- Bulk PR2/5 will switch callers to cad_parcels directly, after which this +-- view can be dropped. -- =========================================================================== DO $$ BEGIN + -- Drop the old table only if cad_parcels (migration target) already exists. + -- Guard prevents accidental drop if J INSERT was skipped on re-run. IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'cad_parcels' ) AND EXISTS ( SELECT 1 FROM information_schema.tables - WHERE table_schema = 'public' AND table_name = 'cad_parcels_geom' + WHERE table_schema = 'public' + AND table_name = 'cad_parcels_geom' + AND table_type = 'BASE TABLE' ) THEN DROP TABLE cad_parcels_geom; END IF; END $$; +-- Create back-compat VIEW (idempotent via CREATE OR REPLACE). +-- Matches original column order and types exactly. +CREATE OR REPLACE VIEW cad_parcels_geom AS + SELECT + cad_num, + geom, + raw_props, + fetched_at + FROM cad_parcels; + +COMMENT ON VIEW cad_parcels_geom IS + 'Back-compat shim: preserves original 4-column signature (cad_num, geom, ' + 'raw_props, fetched_at) for legacy site-finder analyze fallback. ' + 'Bulk PR2/5 will migrate callers to cad_parcels directly; drop this view then.'; + +-- =========================================================================== +-- N. COMMENT ON TABLE for all 8 tables +-- =========================================================================== +COMMENT ON TABLE cad_parcels IS + 'Земельные участки ЕГРН из NSPD (categoryId 36368, EKB кадастровый округ 66:41:*). ' + 'Широкая схема с полными атрибутами NSPD + геометрия. Заменяет cad_parcels_geom.'; + +COMMENT ON TABLE cad_buildings IS + 'Здания ЕГРН из NSPD (categoryId 36369). Широкая схема: objdoc_id для ' + 'tab-group-data lookup, complex_id FK на complexes, cultural_heritage_object BOOLEAN.'; + +COMMENT ON TABLE cad_constructions IS + 'Сооружения из NSPD (categoryId 36383). Объединяет Сооружения и Сооружения ЕГРН ' + '(is_egrn=TRUE). Полный набор технических параметров (высота, объём, протяжённость).'; + +COMMENT ON TABLE cad_oncs IS + 'Объекты незавершённого строительства из NSPD (categoryId 36384, EKB 66:41:*).'; + +COMMENT ON TABLE cad_enk IS + 'Единые недвижимые комплексы из NSPD (categoryId 39663, EKB 66:41:*).'; + +COMMENT ON TABLE cad_zouit IS + 'Зоны с особыми условиями использования территории из NSPD ' + '(categoryIds 36940 / 469039 / 469040 / 469042, все в одной таблице). ' + 'Натуральный ключ: (reg_numb_border, category_id) — один border может входить ' + 'в несколько категорий.'; + +COMMENT ON TABLE cad_quarter_stats IS + 'Агрегированная статистика кадастрового квартала (1-to-1 с cad_quarters_geom). ' + 'Заполняется bulk-harvest worker''ом: счётчики объектов, суммы площадей, кадастровая ' + 'стоимость, real_srid ответа NSPD.'; + +COMMENT ON TABLE cadastre_jobs IS + 'Resumable saga state для bulk harvest runs (один job = один кадастровый квартал ' + 'или batch). Зеркалит паттерн nspd_geo_jobs. heartbeat_at обновляется worker''ом ' + 'каждые N секунд; zombie-детектор переводит зависшие jobs в статус zombie.'; + +-- =========================================================================== +-- O. set_updated_at triggers for all tables with updated_at column. +-- set_updated_at() function already exists in this DB (verified). +-- CREATE OR REPLACE TRIGGER is not available in PG 16 — use DO guard. +-- =========================================================================== + +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_trigger WHERE tgname = 'cad_parcels_set_updated_at' + ) THEN + CREATE TRIGGER cad_parcels_set_updated_at + BEFORE UPDATE ON cad_parcels + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_trigger WHERE tgname = 'cad_buildings_set_updated_at' + ) THEN + CREATE TRIGGER cad_buildings_set_updated_at + BEFORE UPDATE ON cad_buildings + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_trigger WHERE tgname = 'cad_constructions_set_updated_at' + ) THEN + CREATE TRIGGER cad_constructions_set_updated_at + BEFORE UPDATE ON cad_constructions + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_trigger WHERE tgname = 'cad_oncs_set_updated_at' + ) THEN + CREATE TRIGGER cad_oncs_set_updated_at + BEFORE UPDATE ON cad_oncs + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_trigger WHERE tgname = 'cad_enk_set_updated_at' + ) THEN + CREATE TRIGGER cad_enk_set_updated_at + BEFORE UPDATE ON cad_enk + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_trigger WHERE tgname = 'cad_zouit_set_updated_at' + ) THEN + CREATE TRIGGER cad_zouit_set_updated_at + BEFORE UPDATE ON cad_zouit + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + END IF; +END $$; + +DO $$ BEGIN + IF NOT EXISTS ( + SELECT 1 FROM pg_trigger WHERE tgname = 'cad_quarter_stats_set_updated_at' + ) THEN + CREATE TRIGGER cad_quarter_stats_set_updated_at + BEFORE UPDATE ON cad_quarter_stats + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + END IF; +END $$; + +-- cadastre_jobs intentionally excluded — heartbeat_at is the liveness mechanism, +-- not updated_at (which the table does not have). + COMMIT;