117 lines
4.5 KiB
PL/PgSQL
117 lines
4.5 KiB
PL/PgSQL
-- 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
|