fix(cadastre): CAST :geom AS text — psycopg ambig param (#168) #178

Merged
lekss361 merged 1 commit from fix/cadastre-geom-ambiguous-param into main 2026-05-15 13:02:04 +00:00
lekss361 commented 2026-05-15 12:57:33 +00:00 (Migrated from github.com)

Pilot v4 (job 4) failed psycopg AmbiguousParameter on $20 (line CASE WHEN :geom IS NOT NULL). PG can't infer type when bare param used only с IS NOT NULL (type-polymorphic). Fix: wrap in CAST(:geom AS text) per CLAUDE.md pattern (no :x::type trap). 6 upsert helpers.

Pilot v4 (job 4) failed psycopg AmbiguousParameter on `$20` (line `CASE WHEN :geom IS NOT NULL`). PG can't infer type when bare param used only с `IS NOT NULL` (type-polymorphic). Fix: wrap in `CAST(:geom AS text)` per CLAUDE.md pattern (no `:x::type` trap). 6 upsert helpers.
lekss361 commented 2026-05-15 13:01:54 +00:00 (Migrated from github.com)

Review

CI зелёный (backend 1m9s + frontend 1m41s) + cross-check (agent):

Coverage полная

Все 6 upsert helpers обработаны, zero bare :geom в bulk_harvest.py:

  • upsert_parcel (L361,363)
  • upsert_building (L474,476)
  • upsert_construction (L596,597)
  • upsert_onc (L692,693)
  • upsert_enk (L753,754)
  • upsert_zouit (L831,834)

Wider grep по backend/app/api/v1/* и backend/app/services/*: других ST_GeomFromGeoJSON нет — все call sites покрыты.

Cast корректен + None handling intact

  • ST_GeomFromGeoJSON accepts textCAST(:geom AS text) правильно.
  • Param binding (L414,538,660,719,783,876): "geom": geom_json где geom_json = json.dumps(feature.geometry) if feature.geometry else None — всегда str | None.
  • CAST(NULL AS text) IS NOT NULL → false → NULL branch → COALESCE(EXCLUDED.geom, target.geom) сохраняет prior geom. None handling работает.

Precedent

CLAUDE.md паттерн CAST(:x AS type) уже использован в #152 (:weights::jsonb) и #160 (:window_interval::interval). Существующий тест test_upsert_parcel_uses_cast_not_double_colon:268 уже проверяет общий принцип (CAST(:raw_props AS jsonb) есть, ::jsonb/::date отсутствуют). Минор — стоило бы добавить one-liner assert "CAST(:geom AS text)" in sql, но не блокер: AmbiguousParameter — runtime PG error, юнит-тестом на mock'е не ловится; integration/live verification = pilot v5.

Drift отсутствует

Pure SQL string change. Param dict shape, return signatures, RETURNING clause — без изменений. Никаких TS/pydantic схем не трогаем.

approve merge

## Review CI зелёный (backend 1m9s + frontend 1m41s) + cross-check (agent): ### ✅ Coverage полная Все 6 upsert helpers обработаны, **zero bare `:geom`** в `bulk_harvest.py`: - `upsert_parcel` (L361,363) - `upsert_building` (L474,476) - `upsert_construction` (L596,597) - `upsert_onc` (L692,693) - `upsert_enk` (L753,754) - `upsert_zouit` (L831,834) Wider grep по `backend/app/api/v1/*` и `backend/app/services/*`: других `ST_GeomFromGeoJSON` нет — все call sites покрыты. ### ✅ Cast корректен + None handling intact - `ST_GeomFromGeoJSON` accepts `text` → `CAST(:geom AS text)` правильно. - Param binding (L414,538,660,719,783,876): `"geom": geom_json` где `geom_json = json.dumps(feature.geometry) if feature.geometry else None` — всегда `str | None`. - `CAST(NULL AS text) IS NOT NULL` → false → NULL branch → `COALESCE(EXCLUDED.geom, target.geom)` сохраняет prior geom. None handling работает. ### ✅ Precedent CLAUDE.md паттерн `CAST(:x AS type)` уже использован в #152 (`:weights::jsonb`) и #160 (`:window_interval::interval`). Существующий тест `test_upsert_parcel_uses_cast_not_double_colon:268` уже проверяет общий принцип (`CAST(:raw_props AS jsonb)` есть, `::jsonb`/`::date` отсутствуют). Минор — стоило бы добавить one-liner `assert "CAST(:geom AS text)" in sql`, но не блокер: AmbiguousParameter — runtime PG error, юнит-тестом на mock'е не ловится; integration/live verification = pilot v5. ### ✅ Drift отсутствует Pure SQL string change. Param dict shape, return signatures, RETURNING clause — без изменений. Никаких TS/pydantic схем не трогаем. approve merge
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#178
No description provided.