Fix broken drizzle migration chain and add prevention guidelines
The migration system was broken due to three issues: 1. Snapshot 0067 had an invalid `autoincrement` field (PostgreSQL doesn't support this) causing Zod validation to fail with "data is malformed" 2. Migrations 0068 and 0069 were missing snapshot files, breaking the snapshot chain required by `drizzle-kit generate` 3. Orphaned file 0048_mean_enchantress.sql existed outside the journal Fixed by removing the invalid field, regenerating migrations 0068-0069 with proper snapshots via `drizzle-kit generate`, deleting the orphaned file, and adding a no-op verification migration (0070). Made migration 0069 idempotent with IF EXISTS/IF NOT EXISTS guards for production safety. Updated CLAUDE.md with rules to prevent manual migration/journal editing and ensure snapshots are always generated. https://claude.ai/code/session_01JuVHpRPa974MKSHoXNGkgU
This commit is contained in:
parent
a395f10064
commit
651f7d1141
10 changed files with 13446 additions and 41 deletions
17
CLAUDE.md
17
CLAUDE.md
|
|
@ -205,9 +205,20 @@ Do not consider a feature complete until tests are written and passing.
|
|||
|
||||
1. Update `database/schema.ts` with new tables/columns
|
||||
2. Run `npm run db:generate` to create migration
|
||||
3. Run `npm run db:migrate` to apply migration
|
||||
4. Add model functions in `app/models/` for new queries
|
||||
5. Update TypeScript types if needed
|
||||
3. **Verify** the generated migration SQL in `drizzle/` is correct
|
||||
4. **Verify** a corresponding snapshot was created in `drizzle/meta/` (e.g., `0070_snapshot.json`)
|
||||
5. Run `npm run db:migrate` to apply migration
|
||||
6. Add model functions in `app/models/` for new queries
|
||||
7. Update TypeScript types if needed
|
||||
|
||||
**Drizzle Migration Rules (CRITICAL):**
|
||||
- **NEVER** manually create migration SQL files or journal entries. Always use `drizzle-kit generate` or `drizzle-kit generate --custom` so that snapshot files are created automatically.
|
||||
- **NEVER** edit `drizzle/meta/_journal.json` by hand. The journal and snapshots must stay in sync — drizzle-kit manages both.
|
||||
- Every migration **must** have a matching snapshot in `drizzle/meta/`. Missing snapshots cause `drizzle-kit generate` to fail with "data is malformed" errors, which blocks all future migration generation.
|
||||
- Snapshot JSON must only contain PostgreSQL-valid fields. Do not add `"autoincrement"` to column definitions — PostgreSQL uses sequences/serial instead, and invalid fields cause Zod validation failures.
|
||||
- If `drizzle-kit generate` prompts for column renames interactively, use `drizzle-kit generate --custom` instead and write the migration SQL by hand (include data backfill logic as needed).
|
||||
- After generating, run `drizzle-kit generate` again to confirm "No schema changes" — this validates the snapshot chain is healthy.
|
||||
- For destructive migrations (dropping columns, renaming), make the SQL idempotent with `IF EXISTS`/`IF NOT EXISTS` guards where possible.
|
||||
|
||||
### Adding Socket.IO Events
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'ncaam_bracket' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'simulator_type')) THEN
|
||||
ALTER TYPE "public"."simulator_type" ADD VALUE 'ncaam_bracket';
|
||||
END IF;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'ncaaw_bracket' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'simulator_type')) THEN
|
||||
ALTER TYPE "public"."simulator_type" ADD VALUE 'ncaaw_bracket';
|
||||
END IF;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'nba_bracket' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'simulator_type')) THEN
|
||||
ALTER TYPE "public"."simulator_type" ADD VALUE 'nba_bracket';
|
||||
END IF;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "leagues" ADD COLUMN IF NOT EXISTS "discord_webhook_url" varchar(500);
|
||||
|
|
@ -1,19 +1,18 @@
|
|||
-- Step 1: Add new columns as nullable
|
||||
ALTER TABLE "sports_seasons" ADD COLUMN "draft_on" date;--> statement-breakpoint
|
||||
ALTER TABLE "sports_seasons" ADD COLUMN "draft_off" date;--> statement-breakpoint
|
||||
-- Step 1: Add new columns as nullable (IF NOT EXISTS for idempotency)
|
||||
ALTER TABLE "sports_seasons" ADD COLUMN IF NOT EXISTS "draft_on" date;--> statement-breakpoint
|
||||
ALTER TABLE "sports_seasons" ADD COLUMN IF NOT EXISTS "draft_off" date;--> statement-breakpoint
|
||||
|
||||
-- Step 2: Backfill data based on existing is_draftable flag
|
||||
UPDATE "sports_seasons"
|
||||
SET "draft_on" = '2026-04-06', "draft_off" = '2099-12-31'
|
||||
WHERE "is_draftable" = true;--> statement-breakpoint
|
||||
|
||||
UPDATE "sports_seasons"
|
||||
SET "draft_on" = '2020-01-01', "draft_off" = '2020-12-31'
|
||||
WHERE "is_draftable" = false;--> statement-breakpoint
|
||||
-- Step 2: Backfill data based on existing is_draftable flag (only if column still exists)
|
||||
DO $$ BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'sports_seasons' AND column_name = 'is_draftable') THEN
|
||||
UPDATE "sports_seasons" SET "draft_on" = '2026-04-06', "draft_off" = '2099-12-31' WHERE "is_draftable" = true;
|
||||
UPDATE "sports_seasons" SET "draft_on" = '2020-01-01', "draft_off" = '2020-12-31' WHERE "is_draftable" = false;
|
||||
END IF;
|
||||
END $$;--> statement-breakpoint
|
||||
|
||||
-- Step 3: Apply NOT NULL constraints
|
||||
ALTER TABLE "sports_seasons" ALTER COLUMN "draft_on" SET NOT NULL;--> statement-breakpoint
|
||||
ALTER TABLE "sports_seasons" ALTER COLUMN "draft_off" SET NOT NULL;--> statement-breakpoint
|
||||
|
||||
-- Step 4: Drop old column
|
||||
ALTER TABLE "sports_seasons" DROP COLUMN "is_draftable";
|
||||
-- Step 4: Drop old column (IF EXISTS for idempotency)
|
||||
ALTER TABLE "sports_seasons" DROP COLUMN IF EXISTS "is_draftable";
|
||||
|
|
|
|||
3
drizzle/0070_verify_migrations.sql
Normal file
3
drizzle/0070_verify_migrations.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
-- Verification migration: confirms drizzle migration chain is healthy.
|
||||
-- This is a no-op migration that validates the migration system works end-to-end.
|
||||
SELECT 1;
|
||||
|
|
@ -1159,8 +1159,7 @@
|
|||
"name": "world_ranking",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
|
|
|
|||
4465
drizzle/meta/0068_snapshot.json
Normal file
4465
drizzle/meta/0068_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
4470
drizzle/meta/0069_snapshot.json
Normal file
4470
drizzle/meta/0069_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
4470
drizzle/meta/0070_snapshot.json
Normal file
4470
drizzle/meta/0070_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -481,16 +481,23 @@
|
|||
{
|
||||
"idx": 68,
|
||||
"version": "7",
|
||||
"when": 1775000000000,
|
||||
"tag": "0068_cs2_major_simulator",
|
||||
"when": 1775485575205,
|
||||
"tag": "0068_soft_silk_fever",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 69,
|
||||
"version": "7",
|
||||
"when": 1744012800000,
|
||||
"when": 1775485599229,
|
||||
"tag": "0069_draft_window",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 70,
|
||||
"version": "7",
|
||||
"when": 1775485630297,
|
||||
"tag": "0070_verify_migrations",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue