From d761a3795f53384edb2b84a45e7f699525c3a086 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 18 Aug 2026 12:47:14 +0200 Subject: [PATCH] =?UTF-8?q?fix(migrations):=20rend=20la=20migration=20ingr?= =?UTF-8?q?edient=5Ftaxonomy=5Frework=20safe=20sur=20des=20donn=C3=A9es=20?= =?UTF-8?q?existantes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La version générée automatiquement par `prisma migrate diff` castait directement chaque valeur `category` existante (ancien enum à 18 valeurs) vers le nouvel enum à 7 valeurs — échoue pour toute ligne déjà seedée, puisqu'aucune ancienne valeur n'existe dans le nouvel enum. En local ça passait inaperçu (reset complet sur une base vide), mais en production (données déjà seedées) la migration échoue avec "invalid input value for enum". Réécrite pour ajouter les nouvelles colonnes avec une valeur par défaut sûre (aucun cast des données existantes), puis les substituer aux anciennes — même logique que les défauts `@default(...)` déjà documentés dans schema.prisma : seedReferenceData() (relancée à chaque démarrage du conteneur, voir apps/api/Dockerfile) corrige tout de suite après la vraie catégorie/sous-catégorie de chaque ligne. Ajoute aussi un DROP TYPE IF EXISTS défensif : une tentative précédente de cette migration laisse le type IngredientSubcategory orphelin (son CREATE TYPE s'exécute hors de la transaction qui échoue plus loin), une nouvelle tentative sans ce garde-fou échouerait différemment ("type already exists"). Vérifiée en rejouant l'historique complet des migrations sur une base de test jetable, avec des lignes portant les anciennes valeurs d'enum insérées à la main pour reproduire exactement l'échec signalé — la version corrigée s'applique proprement et préserve les id existants (donc toute vraie ligne RecipeIngredient qui y référence). Co-Authored-By: Claude Sonnet 5 --- .../migration.sql | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/apps/api/prisma/migrations/20260818113250_ingredient_taxonomy_rework/migration.sql b/apps/api/prisma/migrations/20260818113250_ingredient_taxonomy_rework/migration.sql index 025b473..b2cc977 100644 --- a/apps/api/prisma/migrations/20260818113250_ingredient_taxonomy_rework/migration.sql +++ b/apps/api/prisma/migrations/20260818113250_ingredient_taxonomy_rework/migration.sql @@ -1,18 +1,38 @@ +-- Rewritten after this migration failed on a database that already had +-- seeded ingredient rows: the original version (auto-generated by `prisma +-- migrate diff`) cast every existing `category` value directly from the +-- old 18-value enum to the new 7-value one, which fails for every row +-- since none of the old values exist in the new enum. This version instead +-- adds the new columns with a safe default (never casting existing data), +-- then swaps them in — the same "add with a default, correct for real on +-- the next seed run" pattern already used for `IngredientCategory`'s and +-- `IngredientSubcategory`'s own `@default(...)` (see their doc comments in +-- schema.prisma). `seedReferenceData()` runs right after `migrate deploy` +-- on every container start (see apps/api/Dockerfile) and corrects every +-- row's real category/subcategory immediately. +-- +-- `DROP TYPE IF EXISTS "IngredientSubcategory"` guards against a previous +-- failed attempt at this exact migration: that CREATE TYPE statement runs +-- outside the AlterEnum transaction below and so persists even though the +-- rest of that failed attempt rolled back — retrying without this guard +-- would hit "type already exists". + -- CreateEnum +DROP TYPE IF EXISTS "IngredientSubcategory"; CREATE TYPE "IngredientSubcategory" AS ENUM ('LEGUMES', 'FRUITS', 'HERBES_FRAICHES', 'VIANDES', 'VOLAILLES', 'POISSONS', 'CRUSTACES_FRUITS_DE_MER', 'FECULENTS', 'LEGUMINEUSES', 'GRAINES_FRUITS_SECS', 'AUTRES', 'PAINS', 'PATES_A_CUIRE', 'PRODUITS_LAITIERS', 'OEUFS', 'ALTERNATIVES', 'EPICES', 'SAUCES', 'ASSAISONNEMENTS', 'BASES', 'EPAISSISSANTS', 'SUCRES'); --- AlterEnum -BEGIN; +-- CreateEnum +DROP TYPE IF EXISTS "IngredientCategory_new"; CREATE TYPE "IngredientCategory_new" AS ENUM ('PRODUITS_FRAIS', 'BOUCHERIE_POISSONNERIE', 'EPICERIE_SECHE', 'BOULANGERIE', 'CREMERIE_FROMAGE', 'CONDIMENTS_EPICES', 'AIDES_CULINAIRES'); -ALTER TABLE "ingredients" ALTER COLUMN "category" DROP DEFAULT; -ALTER TABLE "ingredients" ALTER COLUMN "category" TYPE "IngredientCategory_new" USING ("category"::text::"IngredientCategory_new"); -ALTER TYPE "IngredientCategory" RENAME TO "IngredientCategory_old"; + +-- AlterTable: add the new columns at their defaults — no cast of existing +-- `category` values, so this succeeds regardless of what the table +-- currently holds. +ALTER TABLE "ingredients" ADD COLUMN "category_new" "IngredientCategory_new" NOT NULL DEFAULT 'EPICERIE_SECHE'; +ALTER TABLE "ingredients" ADD COLUMN "subcategory" "IngredientSubcategory" NOT NULL DEFAULT 'AUTRES'; + +-- Swap the old `category` column (old 18-value enum) out for the new one. +ALTER TABLE "ingredients" DROP COLUMN "category"; +ALTER TABLE "ingredients" RENAME COLUMN "category_new" TO "category"; +DROP TYPE "IngredientCategory"; ALTER TYPE "IngredientCategory_new" RENAME TO "IngredientCategory"; -DROP TYPE "IngredientCategory_old"; -ALTER TABLE "ingredients" ALTER COLUMN "category" SET DEFAULT 'EPICERIE_SECHE'; -COMMIT; - --- AlterTable -ALTER TABLE "ingredients" ADD COLUMN "subcategory" "IngredientSubcategory" NOT NULL DEFAULT 'AUTRES', -ALTER COLUMN "category" SET DEFAULT 'EPICERIE_SECHE'; -