36 lines
2.1 KiB
Markdown
36 lines
2.1 KiB
Markdown
|
|
# Database
|
||
|
|
|
||
|
|
## Schema Changes Workflow
|
||
|
|
|
||
|
|
1. Edit `database/schema.ts`
|
||
|
|
2. `npm run db:generate` — generates migration SQL + snapshot
|
||
|
|
3. Verify the SQL in `drizzle/` looks correct
|
||
|
|
4. Verify a snapshot was created in `drizzle/meta/` (e.g. `0070_snapshot.json`)
|
||
|
|
5. `npm run db:migrate` — applies migration
|
||
|
|
6. Add model functions in `app/models/` for new queries
|
||
|
|
|
||
|
|
## Drizzle Migration Rules
|
||
|
|
|
||
|
|
**NEVER** manually create migration SQL files or edit `drizzle/meta/_journal.json`. The journal and snapshots must stay in sync — drizzle-kit manages both. Missing snapshots cause `drizzle-kit generate` to fail with "data is malformed".
|
||
|
|
|
||
|
|
- Always use `drizzle-kit generate` (or `drizzle-kit generate --custom` for interactive rename prompts)
|
||
|
|
- Snapshot JSON must only contain PostgreSQL-valid fields — do not add `"autoincrement"` (Postgres uses sequences, and invalid fields cause Zod validation failures)
|
||
|
|
- After generating, run `drizzle-kit generate` again to confirm "No schema changes" — validates the snapshot chain
|
||
|
|
- For destructive migrations (drop/rename), use `IF EXISTS` / `IF NOT EXISTS` guards
|
||
|
|
|
||
|
|
## Scoring Event Dates (Non-Obvious Gotcha)
|
||
|
|
|
||
|
|
Event dates are stored in **two different columns** depending on event type. Any date-range query must account for both:
|
||
|
|
|
||
|
|
1. **`scoringEvents.eventDate`** (`date` column, `YYYY-MM-DD`) — used for non-bracket events (majors, final standings) and sometimes bracket events when the admin sets one date for an entire round.
|
||
|
|
|
||
|
|
2. **`playoffMatchGames.scheduledAt`** (`timestamp`) — used for individual games within a bracket match. Bracket events often have `eventDate = null` on the scoring event itself.
|
||
|
|
|
||
|
|
**Pattern for date-range queries:** Two-step approach:
|
||
|
|
1. `selectDistinct` event IDs via left joins through `playoffMatches → playoffMatchGames`, filtering: `eventDate IN [dates] OR (scheduledAt >= dayStart AND scheduledAt <= dayEnd)`
|
||
|
|
2. `findMany` on the matched IDs to load full event data with relations
|
||
|
|
|
||
|
|
All timestamp bounds use UTC (`T00:00:00.000Z` / `T23:59:59.999Z`).
|
||
|
|
|
||
|
|
Reference implementation: `getEventsForDates` and `getUpcomingEventsForDraftedParticipants` in `app/models/scoring-event.ts`.
|