3.4 KiB
3.4 KiB
Subagent D — Schema Quality Constraints (pgz_sport.clanovi)
Date: 2026-05-05
Live row count: 3240 (backup retained at pgz_sport.clanovi_backup_20260505_0836 = 3243)
Summary table
| # | Candidate | Type | Pre-flight violators | Status | Object name |
|---|---|---|---|---|---|
| C1 | No internal CamelCase boundary | CHECK | 0 | APPLIED | clanovi_no_camelcase_chk |
| C2 | ime/prezime trimmed | CHECK | 0 | APPLIED | clanovi_trimmed_chk |
| C3 | length(ime) >= 2 AND length(prezime) >= 2 | CHECK | 22 | SKIPPED (see D_violations.md) | — |
| C4 | spol IN ('M','Ž',NULL) | CHECK | 0 | ALREADY PRESENT | clanovi_spol_check (pre-existing) |
| C5 | hns_igrac_id partial UNIQUE | UNIQUE INDEX | 0 dup-groups | APPLIED | clanovi_hns_uniq |
| C6 | (klub_id, lower(ime), lower(prezime), datum_rodenja) UNIQUE | UNIQUE INDEX | 68 dup-groups | SKIPPED (see D_violations.md) | — |
| C7 | BEFORE INSERT/UPDATE normalize trigger | TRIGGER | n/a | APPLIED | clanovi_normalize_trigger + pgz_sport.clanovi_normalize_fn() |
Trigger semantics
clanovi_normalize_fn:
- Always
trim()NEW.imeandNEW.prezime. - On
INSERT, or onUPDATEonly whenimeorprezimeactually change:- reject CamelCase boundary (lenient: only ascii+Croatian-diacritic lower→upper pairs);
- reject
length(ime) < 2orlength(prezime) < 2.
- The "only-when-name-changes" rule preserves the 22 legitimate historical short-name rows (e.g.
id=1852..2141, mostly placeholder'-'ime + surname-only entries) so they can still receiveUPDATEs on other fields.
Smoke insert tests (all wrapped in BEGIN/ROLLBACK so live data unchanged)
| # | Scenario | Expected | Result |
|---|---|---|---|
| 1 | INSERT ('IvoIvic','Test') |
reject (CamelCase) | REJECTED — CamelCase rejected in ime: IvoIvic |
| 2 | INSERT ('PetarPan','Test') |
reject | REJECTED |
| 3 | INSERT (' Ivo ',' Ivić ') |
trim then succeed | INSERTED — stored as ('Ivo','Ivić') |
| 4 | INSERT ('A','Test') |
reject (length) | REJECTED — ime too short (<2 chars): A |
| 5 | INSERT ('Ivan',' X ') |
trim → 'X' len 1 → reject |
REJECTED — prezime too short (<2 chars): X |
| 6 | INSERT ('Marko ',' Marković') |
trim then succeed | INSERTED — stored as ('Marko','Marković') |
| 7 | INSERT duplicate hns_igrac_id='209352' |
reject | REJECTED — duplicate key value violates unique constraint "clanovi_hns_uniq" |
| 8 | 2× NULL + 2× '' hns_igrac_id rows |
all 4 succeed (partial uniqueness ignores NULL/empty) | 4 INSERTS OK |
| 9 | UPDATE id=1852 (ime='-') napomena=... (no name change) |
succeed | UPDATED — short-name row still mutable |
| 10 | UPDATE id=1852 ime='?' (single char) |
reject | REJECTED — ime too short (<2 chars): ? |
All 10 behaviours match expectations. No live row was modified — every test ROLLBACKed.
Final lockdown state on pgz_sport.clanovi
CHECK constraints in force:
clanovi_no_camelcase_chk(NEW)clanovi_trimmed_chk(NEW)clanovi_spol_check(pre-existing)
UNIQUE indexes in force:
clanovi_pkey(id)uq_clanovi_klub_profile(klub_id, profile_url) — pre-existingclanovi_hns_uniq(hns_igrac_id) WHERE not null/empty — NEW
User triggers in force (BEFORE INSERT OR UPDATE):
clanovi_normalize_trigger(NEW)clanovi_validate_source(pre-existing)pgz_sport_clanovi_fts_trg(pre-existing)
Row count unchanged at 3240.