-- pgz_sport.clanovi — schema lockdown DDL (Subagent D) -- Author: dradulic@outlook.com / damir@rinet.one -- Date: 2026-05-05 -- Description: Final, applied DDL. Pre-flight all-clean blocks below were -- committed; SKIPPED candidates (length>=2 CHECK, klub+name+dob -- UNIQUE) are documented in D_violations.md and intentionally -- omitted here. -- -- Row count at apply time: 3240 (live), 3243 (backup_20260505_0836). -- Rollback hints: each block is independent and reversible via -- ALTER TABLE pgz_sport.clanovi DROP CONSTRAINT ...; -- DROP INDEX pgz_sport.clanovi_hns_uniq; -- DROP TRIGGER clanovi_normalize_trigger ON pgz_sport.clanovi; -- DROP FUNCTION pgz_sport.clanovi_normalize_fn(); -- =========================================================================== -- C1: CHECK no internal CamelCase boundary (lower->upper letter pair) -- Pre-flight violators: 0 -- =========================================================================== BEGIN; ALTER TABLE pgz_sport.clanovi ADD CONSTRAINT clanovi_no_camelcase_chk CHECK ( ime !~ '[a-zćčšđžáàâäéèêëíìîïóòôöúùûüñçý][A-ZĆČŠĐŽÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇÝ]' AND prezime !~ '[a-zćčšđžáàâäéèêëíìîïóòôöúùûüñçý][A-ZĆČŠĐŽÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇÝ]' ); COMMIT; -- =========================================================================== -- C2: CHECK ime/prezime are trimmed -- Pre-flight violators: 0 -- =========================================================================== BEGIN; ALTER TABLE pgz_sport.clanovi ADD CONSTRAINT clanovi_trimmed_chk CHECK (ime = trim(ime) AND prezime = trim(prezime)); COMMIT; -- =========================================================================== -- C4: spol values constraint -- NOT applied as new constraint — existing clanovi_spol_check already enforces -- spol IN ('M','Ž',NULL). Documented for completeness. -- CHECK (spol IS NULL OR spol IN ('M','Ž')) -- =========================================================================== -- =========================================================================== -- C5: UNIQUE partial index on hns_igrac_id (non-null, non-empty) -- Pre-flight duplicate groups: 0 -- =========================================================================== BEGIN; CREATE UNIQUE INDEX IF NOT EXISTS clanovi_hns_uniq ON pgz_sport.clanovi (hns_igrac_id) WHERE hns_igrac_id IS NOT NULL AND hns_igrac_id != ''; COMMIT; -- =========================================================================== -- C7: BEFORE INSERT/UPDATE normalize trigger -- Trims ime/prezime, rejects CamelCase, enforces length>=2 only when names -- change (so the existing 22 short-name historical rows can still be UPDATEd -- on other fields without rejection). -- =========================================================================== BEGIN; CREATE OR REPLACE FUNCTION pgz_sport.clanovi_normalize_fn() RETURNS trigger LANGUAGE plpgsql AS $fn$ DECLARE v_changed_name boolean; BEGIN IF NEW.ime IS NOT NULL THEN NEW.ime := trim(NEW.ime); END IF; IF NEW.prezime IS NOT NULL THEN NEW.prezime := trim(NEW.prezime); END IF; IF TG_OP = 'INSERT' THEN v_changed_name := true; ELSE v_changed_name := (NEW.ime IS DISTINCT FROM OLD.ime) OR (NEW.prezime IS DISTINCT FROM OLD.prezime); END IF; IF v_changed_name THEN IF NEW.ime ~ '[a-zćčšđžáàâäéèêëíìîïóòôöúùûüñçý][A-ZĆČŠĐŽÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇÝ]' THEN RAISE EXCEPTION 'CamelCase rejected in ime: %', NEW.ime USING ERRCODE = 'check_violation'; END IF; IF NEW.prezime ~ '[a-zćčšđžáàâäéèêëíìîïóòôöúùûüñçý][A-ZĆČŠĐŽÁÀÂÄÉÈÊËÍÌÎÏÓÒÔÖÚÙÛÜÑÇÝ]' THEN RAISE EXCEPTION 'CamelCase rejected in prezime: %', NEW.prezime USING ERRCODE = 'check_violation'; END IF; IF length(coalesce(NEW.ime, '')) < 2 THEN RAISE EXCEPTION 'ime too short (<2 chars): %', NEW.ime USING ERRCODE = 'check_violation'; END IF; IF length(coalesce(NEW.prezime, '')) < 2 THEN RAISE EXCEPTION 'prezime too short (<2 chars): %', NEW.prezime USING ERRCODE = 'check_violation'; END IF; END IF; RETURN NEW; END; $fn$; DROP TRIGGER IF EXISTS clanovi_normalize_trigger ON pgz_sport.clanovi; CREATE TRIGGER clanovi_normalize_trigger BEFORE INSERT OR UPDATE ON pgz_sport.clanovi FOR EACH ROW EXECUTE FUNCTION pgz_sport.clanovi_normalize_fn(); COMMIT; -- END