Merge pull request 'fix(cadastre): cad_territorial_zones MULTIPOLYGON typmod + ST_Multi coercion (#1344)' (#1673) from fix/cad-zones-multipolygon-1344 into main
Some checks are pending
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / changes (push) Waiting to run
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions

This commit is contained in:
lekss361 2026-06-17 17:45:26 +00:00
commit 11fe22b49a
2 changed files with 76 additions and 6 deletions

View file

@ -555,9 +555,7 @@ async def backfill_parcel_geom(
# Один сбойный квартал не валит весь backfill — лог + продолжаем.
# (WAF 403 пробросится из client и прервёт прогон — это ожидаемо,
# caller-task ловит и не ретраит, как в bulk_harvest.)
logger.warning(
"backfill_parcel_geom: grid-walk failed quarter=%s: %s", quarter, e
)
logger.warning("backfill_parcel_geom: grid-walk failed quarter=%s: %s", quarter, e)
db.rollback()
continue
@ -1477,6 +1475,21 @@ def _save_territorial_zones(db: Session, quarter_cad: str, features: list[dict])
zone_name = props.get("zone_name") or props.get("zone_type_name") or props.get("type_zone")
permitted_use = props.get("permitted_use") or props.get("vri")
# cad_territorial_zones.geom — geography(MultiPolygon, 4326).
# Polygon допустим (ST_Multi обернёт в SQL), но Point/LineString →
# geography-INSERT fail → SAVEPOINT откат → строка дропается молча.
# Зеркалит фильтр upsert_zouit (~1196).
geom_type = geom.get("type") if isinstance(geom, dict) else None
if geom_type not in ("Polygon", "MultiPolygon"):
if geom_type:
logger.info(
"_save_territorial_zones: zone_id=%s geom type=%s (не Polygon/MultiPolygon)"
" — geom=NULL",
zone_id,
geom_type,
)
geom_geojson = None
try:
# begin_nested() требует активной outer-транзакции для SAVEPOINT.
# SQLAlchemy Session (autobegin=True) автоматически начинает tx при первом
@ -1495,9 +1508,11 @@ def _save_territorial_zones(db: Session, quarter_cad: str, features: list[dict])
CAST(:permitted_use AS text),
CAST(:raw_props AS jsonb),
CASE WHEN CAST(:geom AS text) IS NOT NULL
THEN ST_Transform(
ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geom AS text)), 3857),
4326
THEN ST_Multi(
ST_Transform(
ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geom AS text)), 3857),
4326
)
)::geography
ELSE NULL
END

View file

@ -0,0 +1,55 @@
-- 159_alter_cad_territorial_zones_multipolygon.sql
-- ---------------------------------------------------------------------------
-- Контекст:
-- NSPD ПЗЗ территориальные зоны (layer 875838) зачастую возвращают
-- MultiPolygon (у зоны несколько геометрических контуров). Колонка
-- cad_territorial_zones.geom = GEOGRAPHY(POLYGON, 4326) strict → INSERT
-- падал через SAVEPOINT в per-row begin_nested() и строки молча дропались.
--
-- Поведение зеркалит cad_zouit (фикс в 93_cad_parcels_geom_multipolygon.sql
-- как прецедент); патч _save_territorial_zones аналогичен upsert_zouit.
--
-- Действия:
-- 1. ALTER COLUMN geom TYPE geography(MultiPolygon, 4326)
-- USING ST_Multi(geom::geometry)::geography
-- — lossless conversion существующих Polygon-строк в MultiPolygon.
-- 2. DROP + CREATE GIST-индекса (ALTER TYPE требует пересоздания индекса
-- для geography-колонки).
--
-- Idempotent: DO $$...END$$ проверяет текущий type по geometry_columns
-- (geography тоже попадает через postgis_topology_views). Если уже
-- MultiPolygon — пропускает ALTER; GIST пересоздаётся DROP IF EXISTS + IF NOT EXISTS.
--
-- Зависимые VIEW: нет (проверено grep data/sql/ и backend/).
-- Apply after: 158_pzz_zones_ekb_nulls_not_distinct.sql
-- ---------------------------------------------------------------------------
BEGIN;
-- 1. ALTER COLUMN: POLYGON → MULTIPOLYGON (idempotent через geometry_columns)
DO $$
DECLARE
current_type TEXT;
BEGIN
SELECT upper(type) INTO current_type
FROM geometry_columns
WHERE f_table_name = 'cad_territorial_zones'
AND f_geometry_column = 'geom';
IF current_type = 'POLYGON' THEN
ALTER TABLE cad_territorial_zones
ALTER COLUMN geom TYPE geography(MultiPolygon, 4326)
USING ST_Multi(geom::geometry)::geography;
RAISE NOTICE 'cad_territorial_zones.geom converted POLYGON -> MULTIPOLYGON (rows: %)',
(SELECT COUNT(*) FROM cad_territorial_zones WHERE geom IS NOT NULL);
ELSE
RAISE NOTICE 'cad_territorial_zones.geom type=% — skip ALTER', current_type;
END IF;
END$$;
-- 2. GIST индекс — DROP + CREATE (тип column изменился, индекс пересоздаём)
DROP INDEX IF EXISTS cad_territorial_zones_geom_gist;
CREATE INDEX IF NOT EXISTS cad_territorial_zones_geom_gist
ON cad_territorial_zones USING GIST (geom);
COMMIT;