# Outillage de comparaison de schéma

Créé pour l'issue #137. Sert à répondre à **une seule question**, mais correctement :

> le schéma réel d'une base est-il celui que produisent nos migrations ?

## Pourquoi cet outil existe

Le 06/08/2026, la table `migrations` de la production a été recalée en comparant des **noms** :
un `comm` entre les fichiers du disque et les lignes de la table a conclu « tout est là ». Le 08/08,
`courses.category_id` s'est révélée absente alors que sa migration était enregistrée.

**Comparer des noms de migrations ne prouve rien.** Une migration enregistrée peut n'avoir jamais
été exécutée ; une migration exécutée peut avoir produit un objet différent de ce que dit le code
aujourd'hui. Seule la comparaison **objet par objet** conclut.

## Usage

```bash
# 1. Schéma de référence — ce que le code croit être le schéma
mysql -u root -e "DROP DATABASE IF EXISTS efektiv_refschema; CREATE DATABASE efektiv_refschema CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
DB_DATABASE=efektiv_refschema /opt/homebrew/opt/php@8.2/bin/php artisan migrate:fresh --force

# 2. Extraction (structure seule — LECTURE SEULE, aucune écriture sur la cible)
sed 's/@@DB@@/efektiv_refschema/g' extract_schema.sql | mysql -u root -N --batch > /tmp/ref.tsv
sed 's/@@DB@@/<base-cible>/g'      extract_schema.sql | mysql -u root -N --batch > /tmp/cible.tsv

# 3. Comparaison
python3 diff_schema.py /tmp/ref.tsv /tmp/cible.tsv
```

La sortie est un JSON à huit entrées : tables et colonnes manquantes / en trop / divergentes,
index manquants / en trop, clés étrangères manquantes, plus les moteurs et les compteurs.

## Ce que l'outil neutralise (et ce qu'il ne neutralise pas)

**Neutralisé** — écarts de *représentation* sans portée fonctionnelle, qui produiraient des
faux positifs en masse :

- largeur d'affichage des entiers (`int(11)` → `int`, supprimée en MySQL 8.0.19+) ;
- quotage et format des valeurs par défaut (`'0'`, `0`, `0.00`) ;
- `CURRENT_TIMESTAMP` / `now()` ;
- mention `DEFAULT_GENERATED` dans `EXTRA` ;
- **nom** des index : ils sont appariés par (colonnes, unicité), pas par nom, les noms
  auto-générés pouvant différer d'une version à l'autre.

**Non neutralisé, donc rapporté** — tout ce qui a une portée fonctionnelle : type de base,
nullabilité, valeur par défaut réelle, collation, présence d'un index ou d'une clé étrangère.

## Limites à connaître

- **MyISAM ignore les clés étrangères.** Sur une base MyISAM, `fks_missing_in_prod` liste
  l'intégralité des FK de la référence : c'est attendu, ce n'est pas 64 anomalies distinctes
  (cf. issue #136). À traiter comme un sujet à part, jamais comme du bruit.
- L'outil compare des **structures**, pas des **données**. Un resserrage de nullabilité ou un
  rétrécissement de type demande une vérification en données *avant* application (compter les NULL,
  mesurer les longueurs) — cf. `docs/roadmap/19_DIFF_SCHEMA_PROD.md` §5.
- Le comparateur n'inspecte ni les vues, ni les triggers, ni les procédures stockées : le projet
  n'en utilise pas. À étendre le jour où ce ne sera plus vrai.
