Explicitní null v INSERTu přebije DEFAULT sloupce → NOT NULL violation
import { Aside } from ‘@astrojs/starlight/components’;
Symptom
Sekce “Symptom”Objednávkový endpoint padá s null value in column "gallery_id" of relation "…" violates not-null constraint — ale jen pro NĚKTERÉ typy objednávek. Jiné typy (ty „nové”, kvůli kterým se sloupec zaváděl) fungují. Sloupec přitom má DEFAULT 1, takže „by to přece mělo projít i bez hodnoty”.
Root cause
Sekce “Root cause”Nová feature (rezervace s vazbou na konkrétní entitu) přidala do sdíleného INSERTu vypočtený sloupec:
let exhibitionGalleryId: number | null = null;if (isExhibitionSlug(service.slug)) { exhibitionGalleryId = galleryIdForServiceSlug(service.slug); // jen pro výstavy}// ...await db.from('promo_orders').insert({ artist_id: user.id, gallery_id: exhibitionGalleryId, // ❌ pro ne-výstavní služby EXPLICITNÍ null ...});Rozdíl, na kterém to stojí: DEFAULT se uplatní jen když sloupec v INSERTu chybí. Explicitně poslané NULL je legitimní hodnota, DEFAULT nepřepíše — a na NOT NULL sloupci shodí constraint. Vývojář feature testoval jen „svou” větev (výstavy, kde helper vrací číslo); všechny ostatní větve sdíleného INSERTu tiše rozbil.
Fix
Sekce “Fix”Fallback přímo v insertu (nebo klíč vynechat, když je null):
gallery_id: exhibitionGalleryId ?? 1, // ✅ ne-výstavní služby patří defaultní entitěJak se tomu vyvarovat v jiných systémech
Sekce “Jak se tomu vyvarovat v jiných systémech”- Detection: grep INSERTů na vzor
sloupec: nullableHelper(u sloupců s NOT NULL. V code review otázka: „může tahle proměnná být null, a má sloupec DEFAULT?” Test MUSÍ pokrýt i větve, kde se helper nevyhodnotí (tj. ostatní typy objednávek, ne jen ten nový). - Anti-pattern: jeden sdílený INSERT pro víc typů záznamů + nový sloupec relevantní jen pro jeden typ, dosazovaný napřímo z
X | nullproměnné. - Lepší přístup: buduj insert objekt podmíněně (
...(id != null && { gallery_id: id })), nebo drž fallback na DB default hodnotu explicitně v kódu. A po nasazení feature ověř i JINÉ typy záznamů, které sdílejí stejný INSERT.
Sister bugs / související
Sekce “Sister bugs / související”Stejná past v UPDATE: col = COALESCE($1, col) vs. přímé dosazení null. A obráceně — spolehnutí na DEFAULT tam, kde ORM (Prisma apod.) posílá všechny sloupce vždy, takže DEFAULT nikdy nenastoupí.