003e419a93
- Migration FK CASCADE/SET NULL pour toutes les FK vers pieces.id (miroir de la fix Composant) + cleanup des orphelins existants avec audit log - Helper ensurePieceExists() qui catch EntityNotFoundException dans MachineStructureController et CustomFieldValueController - Script SQL standalone scripts/cleanup_orphan_piece_refs.sql pour nettoyer la prod sans attendre la migration - Affiche les machines (avec leur site) utilisant la pièce avant la confirmation de suppression
224 lines
7.6 KiB
PL/PgSQL
224 lines
7.6 KiB
PL/PgSQL
-- =============================================================================
|
|
-- cleanup_orphan_piece_refs.sql
|
|
-- =============================================================================
|
|
-- Contexte : la suppression directe de rows dans `pieces` (bypass Doctrine /
|
|
-- FK DB sans ON DELETE CASCADE) laisse des références orphelines dans plusieurs
|
|
-- tables, ce qui fait planter l'API au chargement d'une Machine :
|
|
-- Doctrine\ORM\EntityNotFoundException: Entity of type 'App\Entity\Piece' ...
|
|
--
|
|
-- Ce script fait deux choses :
|
|
-- 1. ÉTAPE 1 (toujours exécutée) : compte les références orphelines par table
|
|
-- pour visualiser l'ampleur du problème.
|
|
-- 2. ÉTAPE 2 (commentée par défaut) : insère un audit_log par row, puis
|
|
-- DELETE / UPDATE SET NULL selon la sémantique attendue côté entité.
|
|
-- Décommenter le bloc `BEGIN; ... COMMIT;` pour appliquer.
|
|
--
|
|
-- Usage :
|
|
-- # Dry-run (compte seulement)
|
|
-- psql -h <host> -U <user> -d inventory -f scripts/cleanup_orphan_piece_refs.sql
|
|
--
|
|
-- # Application : décommenter le bloc transactionnel en bas du fichier,
|
|
-- # puis relancer la même commande. La transaction garantit l'atomicité.
|
|
-- =============================================================================
|
|
|
|
|
|
-- ============================== ÉTAPE 1 : DRY-RUN ============================
|
|
\echo ''
|
|
\echo '=== Orphelins par table (Pieces) ==='
|
|
|
|
SELECT 'machine_piece_links' AS table_name, count(*) AS orphans
|
|
FROM machine_piece_links
|
|
WHERE pieceid IS NOT NULL
|
|
AND pieceid NOT IN (SELECT id FROM pieces)
|
|
UNION ALL
|
|
SELECT 'composant_piece_slots', count(*)
|
|
FROM composant_piece_slots
|
|
WHERE selectedpieceid IS NOT NULL
|
|
AND selectedpieceid NOT IN (SELECT id FROM pieces)
|
|
UNION ALL
|
|
SELECT 'piece_product_slots', count(*)
|
|
FROM piece_product_slots
|
|
WHERE pieceid NOT IN (SELECT id FROM pieces)
|
|
UNION ALL
|
|
SELECT 'documents', count(*)
|
|
FROM documents
|
|
WHERE pieceid IS NOT NULL
|
|
AND pieceid NOT IN (SELECT id FROM pieces)
|
|
UNION ALL
|
|
SELECT 'custom_field_values', count(*)
|
|
FROM custom_field_values
|
|
WHERE pieceid IS NOT NULL
|
|
AND pieceid NOT IN (SELECT id FROM pieces)
|
|
UNION ALL
|
|
SELECT 'piece_constructeur_links', count(*)
|
|
FROM piece_constructeur_links
|
|
WHERE pieceid NOT IN (SELECT id FROM pieces)
|
|
UNION ALL
|
|
SELECT 'piece_products', count(*)
|
|
FROM piece_products
|
|
WHERE piece_id NOT IN (SELECT id FROM pieces)
|
|
ORDER BY table_name;
|
|
|
|
\echo ''
|
|
\echo '=> Pour appliquer le cleanup, décommenter le bloc BEGIN/COMMIT ci-dessous.'
|
|
\echo ''
|
|
|
|
|
|
-- ============================== ÉTAPE 2 : APPLY =============================
|
|
-- Décommenter ce bloc pour exécuter le cleanup. La transaction garantit
|
|
-- l'atomicité : tout passe, ou rien (en cas d'erreur, ROLLBACK auto).
|
|
--
|
|
-- BEGIN;
|
|
--
|
|
-- -- 1. Audit log : snapshot des rows qui vont être supprimées (traçabilité prod).
|
|
--
|
|
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
|
|
-- SELECT
|
|
-- 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
|
|
-- 'machine_piece_link',
|
|
-- l.id,
|
|
-- 'delete',
|
|
-- json_build_object(
|
|
-- 'id', l.id,
|
|
-- 'machineId', l.machineid,
|
|
-- 'pieceId', l.pieceid,
|
|
-- 'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
|
|
-- ),
|
|
-- NULL,
|
|
-- NOW()
|
|
-- FROM machine_piece_links l
|
|
-- WHERE l.pieceid IS NOT NULL
|
|
-- AND l.pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
|
|
-- SELECT
|
|
-- 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
|
|
-- 'piece_product_slot',
|
|
-- s.id,
|
|
-- 'delete',
|
|
-- json_build_object(
|
|
-- 'id', s.id,
|
|
-- 'pieceId', s.pieceid,
|
|
-- 'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
|
|
-- ),
|
|
-- NULL,
|
|
-- NOW()
|
|
-- FROM piece_product_slots s
|
|
-- WHERE s.pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
|
|
-- SELECT
|
|
-- 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
|
|
-- 'document',
|
|
-- d.id,
|
|
-- 'delete',
|
|
-- json_build_object(
|
|
-- 'id', d.id,
|
|
-- 'name', d.name,
|
|
-- 'filename', d.filename,
|
|
-- 'pieceId', d.pieceid,
|
|
-- 'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
|
|
-- ),
|
|
-- NULL,
|
|
-- NOW()
|
|
-- FROM documents d
|
|
-- WHERE d.pieceid IS NOT NULL
|
|
-- AND d.pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
|
|
-- SELECT
|
|
-- 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
|
|
-- 'custom_field_value',
|
|
-- v.id,
|
|
-- 'delete',
|
|
-- json_build_object(
|
|
-- 'id', v.id,
|
|
-- 'pieceId', v.pieceid,
|
|
-- 'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
|
|
-- ),
|
|
-- NULL,
|
|
-- NOW()
|
|
-- FROM custom_field_values v
|
|
-- WHERE v.pieceid IS NOT NULL
|
|
-- AND v.pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
|
|
-- SELECT
|
|
-- 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
|
|
-- 'piece_constructeur_link',
|
|
-- l.id,
|
|
-- 'delete',
|
|
-- json_build_object(
|
|
-- 'id', l.id,
|
|
-- 'pieceId', l.pieceid,
|
|
-- 'constructeurId', l.constructeurid,
|
|
-- 'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
|
|
-- ),
|
|
-- NULL,
|
|
-- NOW()
|
|
-- FROM piece_constructeur_links l
|
|
-- WHERE l.pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- -- 2. Nettoyage des orphelins.
|
|
--
|
|
-- DELETE FROM machine_piece_links
|
|
-- WHERE pieceid IS NOT NULL
|
|
-- AND pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- UPDATE composant_piece_slots SET selectedpieceid = NULL
|
|
-- WHERE selectedpieceid IS NOT NULL
|
|
-- AND selectedpieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- DELETE FROM piece_product_slots
|
|
-- WHERE pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- DELETE FROM documents
|
|
-- WHERE pieceid IS NOT NULL
|
|
-- AND pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- DELETE FROM custom_field_values
|
|
-- WHERE pieceid IS NOT NULL
|
|
-- AND pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- DELETE FROM piece_constructeur_links
|
|
-- WHERE pieceid NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- DELETE FROM piece_products
|
|
-- WHERE piece_id NOT IN (SELECT id FROM pieces);
|
|
--
|
|
-- -- 3. Vérification post-cleanup : tout doit être à 0.
|
|
-- SELECT 'machine_piece_links' AS table_name, count(*) AS remaining_orphans
|
|
-- FROM machine_piece_links
|
|
-- WHERE pieceid IS NOT NULL
|
|
-- AND pieceid NOT IN (SELECT id FROM pieces)
|
|
-- UNION ALL
|
|
-- SELECT 'composant_piece_slots', count(*)
|
|
-- FROM composant_piece_slots
|
|
-- WHERE selectedpieceid IS NOT NULL
|
|
-- AND selectedpieceid NOT IN (SELECT id FROM pieces)
|
|
-- UNION ALL
|
|
-- SELECT 'piece_product_slots', count(*)
|
|
-- FROM piece_product_slots
|
|
-- WHERE pieceid NOT IN (SELECT id FROM pieces)
|
|
-- UNION ALL
|
|
-- SELECT 'documents', count(*)
|
|
-- FROM documents
|
|
-- WHERE pieceid IS NOT NULL
|
|
-- AND pieceid NOT IN (SELECT id FROM pieces)
|
|
-- UNION ALL
|
|
-- SELECT 'custom_field_values', count(*)
|
|
-- FROM custom_field_values
|
|
-- WHERE pieceid IS NOT NULL
|
|
-- AND pieceid NOT IN (SELECT id FROM pieces)
|
|
-- UNION ALL
|
|
-- SELECT 'piece_constructeur_links', count(*)
|
|
-- FROM piece_constructeur_links
|
|
-- WHERE pieceid NOT IN (SELECT id FROM pieces)
|
|
-- UNION ALL
|
|
-- SELECT 'piece_products', count(*)
|
|
-- FROM piece_products
|
|
-- WHERE piece_id NOT IN (SELECT id FROM pieces)
|
|
-- ORDER BY table_name;
|
|
--
|
|
-- COMMIT;
|