fix(tradein-estimator): restore CAST in IS NOT NULL predicates (revert audit #9)

PR #509 removed CAST(:target_year AS integer) and CAST(:target_house_type AS text)
from the IS NOT NULL predicates in _fetch_analogs, citing audit finding #9 (perf).
This broke production: psycopg3 prepared statement could not determine $4 type
when target_house_type=None, raising AmbiguousParameter at plan time. The CAST
in the THEN branch is not enough — PG planner needs the type to evaluate
IS NOT NULL itself.

The inline comment at lines 814-816 explicitly documented this requirement;
PR #509 misread the audit finding. Per-row CAST cost on ≤300 rows is negligible
vs production breakage. Restoring CAST forms; THEN-branch CASTs untouched
(they were never removed).

Verified prod stack trace 2026-05-24 ~12:25 (target_year=1978, target_house_type=None).
This commit is contained in:
lekss361 2026-05-24 15:28:36 +03:00
parent ac57b8e905
commit 4a2a804296

View file

@ -881,13 +881,13 @@ def _fetch_analogs(
-- без типа PostgreSQL "could not determine data type of parameter" -- без типа PostgreSQL "could not determine data type of parameter"
-- (AmbiguousParameter). Явный тип снимает неоднозначность. -- (AmbiguousParameter). Явный тип снимает неоднозначность.
+ CASE + CASE
WHEN :target_year IS NOT NULL WHEN CAST(:target_year AS integer) IS NOT NULL
AND year_built IS NOT NULL AND year_built IS NOT NULL
THEN abs(year_built - CAST(:target_year AS integer)) / 12.0 THEN abs(year_built - CAST(:target_year AS integer)) / 12.0
ELSE 0 ELSE 0
END END
+ CASE + CASE
WHEN :target_house_type IS NOT NULL WHEN CAST(:target_house_type AS text) IS NOT NULL
AND house_type IS NOT NULL AND house_type IS NOT NULL
AND house_type <> CAST(:target_house_type AS text) AND house_type <> CAST(:target_house_type AS text)
THEN 1.5 THEN 1.5
@ -900,13 +900,13 @@ def _fetch_analogs(
ST_Distance(geom::geography, ST_MakePoint(:lon, :lat)::geography) ST_Distance(geom::geography, ST_MakePoint(:lon, :lat)::geography)
/ 1000.0 / 1000.0
+ CASE + CASE
WHEN :target_year IS NOT NULL WHEN CAST(:target_year AS integer) IS NOT NULL
AND year_built IS NOT NULL AND year_built IS NOT NULL
THEN abs(year_built - CAST(:target_year AS integer)) / 12.0 THEN abs(year_built - CAST(:target_year AS integer)) / 12.0
ELSE 0 ELSE 0
END END
+ CASE + CASE
WHEN :target_house_type IS NOT NULL WHEN CAST(:target_house_type AS text) IS NOT NULL
AND house_type IS NOT NULL AND house_type IS NOT NULL
AND house_type <> CAST(:target_house_type AS text) AND house_type <> CAST(:target_house_type AS text)
THEN 1.5 THEN 1.5