-- ===================================================================== -- 058_integrity_keys.sql -- Hardening integrità chiavi — estende l'audit della mig.039 alle tabelle -- aggiunte dopo (moduli 040-057: stakeholder, ISO-readiness, KB, ...). -- -- DOC-RECORD del DDL applicato dal runner idempotente GUARDATO: -- application/cli/migrate_058_integrity_keys.php -- (pre-check duplicati/orfani su information_schema + dato reale, try/catch -- su 1061/1826/1062). NON eseguire ciecamente questo .sql su un DB migrato. -- -- Verificato sul dato reale (db_integrity_probe.php, 2026-06-18, nis2_agile_db): -- 103 tabelle, tutte con PK, tutte InnoDB, 196 FK pre-esistenti. -- 0 duplicati e 0 orfani su tutte le voci TIER 1 (sotto). -- ===================================================================== -- ── TIER 1: UNIQUE su chiavi naturali (additivo, nessuna modifica dato) ── -- Codici progressivi per-org (race su create concorrenti, ora con retry-on-1062 -- nei rispettivi controller). periodic_controls aveva già uk_pctl_code (mig.057). ALTER TABLE internal_audits ADD UNIQUE KEY uk_intaud_code (organization_id, code); ALTER TABLE management_reviews ADD UNIQUE KEY uk_mgr_code (organization_id, code); -- 1:1 con il documento vettoriale in Qdrant (16 righe, 0 dup). ALTER TABLE kb_uploaded_documents ADD UNIQUE KEY uk_kbdoc_qdrant (qdrant_doc_uuid); -- Token di tracking segnalazione anonima = chiave di retrieval (WHERE token=?). -- Sostituisce l'indice non-unique idx_token ridondante. ALTER TABLE whistleblowing_reports DROP INDEX idx_token; ALTER TABLE whistleblowing_reports ADD UNIQUE KEY uq_wb_anon_token (anonymous_token); -- ── TIER 1: FOREIGN KEY mancanti (stesso-DB, 0 orfani verificati) ── -- consulting_firm_id: la mig.039 aggiunse solo la FK su firm_org_assignments. ALTER TABLE organizations ADD CONSTRAINT fk_org_consulting_firm FOREIGN KEY (consulting_firm_id) REFERENCES consulting_firms(id) ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE users ADD CONSTRAINT fk_users_consulting_firm FOREIGN KEY (consulting_firm_id) REFERENCES consulting_firms(id) ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE kb_uploaded_documents ADD CONSTRAINT fk_kbdoc_consulting_firm FOREIGN KEY (consulting_firm_id) REFERENCES consulting_firms(id) ON DELETE SET NULL ON UPDATE CASCADE; -- link nullable verso entità dello stesso DB (SET NULL coerente con lo schema). ALTER TABLE isms_soa ADD CONSTRAINT fk_isms_soa_control FOREIGN KEY (linked_control_id) REFERENCES compliance_controls(id) ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE isms_documents ADD CONSTRAINT fk_isms_doc_policy FOREIGN KEY (linked_policy_id) REFERENCES policies(id) ON DELETE SET NULL ON UPDATE CASCADE; ALTER TABLE management_review_decisions ADD CONSTRAINT fk_mrd_capa FOREIGN KEY (capa_id) REFERENCES capa_actions(id) ON DELETE SET NULL ON UPDATE CASCADE; -- ===================================================================== -- TIER 2 — bonifica righe STALE + FK (richiede --with-cleanup nel runner; -- CANCELLA righe orfane → conferma esplicita necessaria) -- -- C1) firm_org_assignments: 9 righe (consulting_firm 1 "Agile", dogfooding, -- create 2026-05-29) verso org 126/127/128/129 ELIMINATE nella pulizia -- demo del 2026-06-12. Risolve il "deferred-b" della mig.039. -- DELETE FROM firm_org_assignments WHERE organization_id NOT IN (SELECT id FROM organizations); -- ALTER TABLE firm_org_assignments ADD CONSTRAINT fk_firm_org_assignments_organization_id -- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE; -- -- C2) active_sessions: 7 sessioni verso org inesistenti (effimere). -- DELETE FROM active_sessions WHERE organization_id<>0 AND organization_id NOT IN (SELECT id FROM organizations); -- ALTER TABLE active_sessions ADD CONSTRAINT fk_active_sessions_organization_id -- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE ON UPDATE CASCADE; -- ===================================================================== -- ── NON TOCCATE — pattern "by design" (documentato, NESSUNA azione) ── -- • audit_logs.organization_id: 1996 "orfani" + 408 org_id=0 → audit trail -- IMMUTABILE (trigger mig.006) che DEVE sopravvivere alla cancellazione org. -- Nessuna FK per scelta architetturale. -- • supplier_categories.organization_id = 0: sentinel "categoria di sistema" -- (deferred-a mig.039). -- • Colonne *_id polimorfiche (object_id, entity_id, source_entity_id, ...) e -- cross-DB (sso_identity_id, lg231_company_id/order_id): nessuna FK possibile.