fix(db): default cad_parcels.source in trigger (on-demand ingest) #1043
1 changed files with 71 additions and 0 deletions
71
data/sql/130_cad_parcels_default_source.sql
Normal file
71
data/sql/130_cad_parcels_default_source.sql
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
-- 130_cad_parcels_default_source.sql
|
||||
-- FIX (prod, продолжение #129) — on-demand НСПД-ингест cad_parcels падает на ВТОРОЙ NOT NULL
|
||||
-- колонке `source` (после того как #129 закрыл quarter_cad_number).
|
||||
--
|
||||
-- БАГ (ground-truth, измерено на prod после деплоя #129):
|
||||
-- cad_parcels NOT NULL колонки: cad_num, quarter_cad_number, category_id, source,
|
||||
-- fetched_at, updated_at. #129-триггер достроил quarter_cad_number, но on-demand-ингест
|
||||
-- (bulk_harvest.upsert_parcel / parcels-fetch воркер для не-предзагруженных участков)
|
||||
-- доезжает до INSERT с source=NULL →
|
||||
-- psycopg.errors.NotNullViolation: null value in column "source" of relation "cad_parcels"
|
||||
-- → строка падает → geometry-транзакция роллбэчится → cad_parcels_geom тоже не сохраняется
|
||||
-- → POST /analyze для участка навсегда залипает «Геометрия загружается из НСПД…».
|
||||
-- Воспроизведено на РЕАЛЬНОМ участке 66:41:0610029:83 (job 57: упавшая строка несёт
|
||||
-- quarter_cad_number=66:41:0610029 (достроен #129) + валидный geom, null только в source).
|
||||
--
|
||||
-- Все три code-вызывающих upsert_features передают непустой source ("search",
|
||||
-- "search_per_cat", "wms_grid_walk"), т.е. в локальном коде source не None — но в проде
|
||||
-- до INSERT доезжает NULL (как и с quarter_cad_number, путь ускользает: вероятно отдельная
|
||||
-- ветка/стейл-фронт grid-walk). Вместо охоты за каждым код-путём — гарантируем на уровне БД,
|
||||
-- как для quarter_cad_number: source — метаданные происхождения, для on-demand НСПД-фетча
|
||||
-- осмысленный дефолт 'nspd'.
|
||||
--
|
||||
-- ПОЧЕМУ ТРИГГЕР (а не DEFAULT): колоночный DEFAULT срабатывает только когда колонка ОПУЩЕНА
|
||||
-- из INSERT; здесь INSERT биндит source ЯВНЫМ NULL → DEFAULT не применился бы. BEFORE-триггер
|
||||
-- же переопределяет явный NULL. Расширяем СУЩЕСТВУЮЩУЮ функцию #129 (CREATE OR REPLACE) — сам
|
||||
-- триггер cad_parcels_derive_quarter_cad (BEFORE INSERT OR UPDATE, #129) уже навешен, меняем
|
||||
-- только тело функции. Один инвариант-страж для всех NOT NULL метаданных, выводимых/дефолтимых.
|
||||
--
|
||||
-- ПОВЕДЕНИЕ (дополнение к #129):
|
||||
-- • quarter_cad_number IS NULL + cad_num ≥3 сегмента → достроить (как #129).
|
||||
-- • source IS NULL → 'nspd' (осмысленный дефолт происхождения для on-demand НСПД-ингеста;
|
||||
-- не маскирует данные — это просто метка источника, явный source от bulk-загрузчиков
|
||||
-- ("search"/"wms_grid_walk"/...) уважается, перетираем ТОЛЬКО NULL).
|
||||
--
|
||||
-- Idempotent: CREATE OR REPLACE FUNCTION (переопределяет тело на месте). Триггер не трогаем
|
||||
-- (#129 его создал). BEGIN/COMMIT. Никаких DROP TABLE/TRUNCATE/строк.
|
||||
-- Deploy order: чистая СХЕМА, без зависимости от кода. Эффект мгновенный со следующего ингеста:
|
||||
-- on-demand cad_parcels INSERT больше не падает на NOT NULL (quarter+source достраиваются) →
|
||||
-- geometry персистится → реальный новый участок анализируется.
|
||||
|
||||
BEGIN;
|
||||
|
||||
CREATE OR REPLACE FUNCTION cad_parcels_derive_quarter_cad() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- #129: достроить quarter_cad_number из cad_num (первые 3 colon-сегмента), если NULL.
|
||||
IF NEW.quarter_cad_number IS NULL
|
||||
AND NEW.cad_num IS NOT NULL
|
||||
AND array_length(string_to_array(NEW.cad_num, ':'), 1) >= 3
|
||||
THEN
|
||||
NEW.quarter_cad_number :=
|
||||
array_to_string((string_to_array(NEW.cad_num, ':'))[1:3], ':');
|
||||
END IF;
|
||||
|
||||
-- #130: source NOT NULL — on-demand-ингест доезжает с NULL. Дефолт 'nspd' (метка
|
||||
-- источника). Перетираем ТОЛЬКО NULL → явные source ("search"/"wms_grid_walk"/...) целы.
|
||||
IF NEW.source IS NULL THEN
|
||||
NEW.source := 'nspd';
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION cad_parcels_derive_quarter_cad() IS
|
||||
'BEFORE INSERT OR UPDATE-триггер cad_parcels (#129+#130): достраивает NOT NULL '
|
||||
'quarter_cad_number из cad_num когда NULL, и дефолтит NOT NULL source→''nspd'' когда NULL. '
|
||||
'Чинит NotNullViolation во ВСЕХ путях записи on-demand НСПД-ингеста (репро 66:41:0610029:83). '
|
||||
'Явные значения уважаются — перетираются только NULL. <3-сегм cad_num → quarter остаётся '
|
||||
'NULL (NOT NULL отклонит реальный мусор).';
|
||||
|
||||
COMMIT;
|
||||
Loading…
Add table
Reference in a new issue