58 lines
3.6 KiB
Markdown
58 lines
3.6 KiB
Markdown
# Contract: Database Patterns (Go / MySQL / MariaDB)
|
|
|
|
Version: 1.9
|
|
|
|
See `README.md` for examples, migration snippets, and Docker test commands.
|
|
|
|
## Query and Startup Rules
|
|
|
|
- Never execute SQL on the same transaction while iterating an open result cursor. Use a two-phase flow: read all rows, close the cursor, then execute writes.
|
|
- This rule applies to `database/sql`, GORM transactions, and any repository call made while another cursor in the same transaction is still open.
|
|
- User-visible records use soft delete or archive flags. Do not hard-delete records with history or foreign-key references.
|
|
- Archive operations must be reversible from the UI.
|
|
- Use `gorm:"-"` only for fields that must be ignored entirely. Use `gorm:"-:migration"` for fields populated by queries but excluded from migrations.
|
|
- Always verify the DB connection before starting the HTTP server. Never serve traffic with an unverified DB connection.
|
|
- Prevent N+1 queries. Do not query inside loops over rows from another query; use JOINs or batched `IN (...)` queries.
|
|
|
|
## Migration and Backup Rules
|
|
|
|
- The migration engine owns backup creation. The operator must never be required to take a manual pre-migration backup.
|
|
- Backup storage, retention, archive format, and restore-readiness must follow `backup-management`.
|
|
- Before applying any unapplied migrations, take and verify a full DB backup.
|
|
- Before applying a migration step that changes a table, take a targeted backup of each affected table.
|
|
- Before writing any backup, verify that the output path resolves outside the git worktree and is not tracked or staged in git.
|
|
- If any migration step in a session fails, roll back all steps applied in that session in reverse order.
|
|
- If rollback is not sufficient, restore from the targeted backup taken before the failing step.
|
|
- After rollback or restore, the DB must be back in the same state it had before the session started.
|
|
- Migration failures must emit structured diagnostics naming the failed step, rollback actions, and final DB state.
|
|
|
|
## Migration Authoring Rules
|
|
|
|
- For local-first desktop apps, migration recovery must also follow `local-first-recovery`.
|
|
- Migrations are sequential and immutable after merge.
|
|
- Each migration should be reversible where possible.
|
|
- Do not rename a column in one step. Add new, backfill, and drop old across separate deploys.
|
|
- Auto-apply on startup is allowed for internal tools only if the behavior is documented.
|
|
- Every `.sql` migration file must start with:
|
|
- `-- Tables affected: ...`
|
|
- `-- recovery.not-started: ...`
|
|
- `-- recovery.partial: ...`
|
|
- `-- recovery.completed: ...`
|
|
- one or more `-- verify: <description> | <SQL>` checks
|
|
- Verify queries must return rows only when something is wrong.
|
|
- Verify queries must exclude NULL and empty values when those would create false positives.
|
|
- A migration is recorded as applied only after all verify checks pass.
|
|
|
|
## Pre-Production Validation Rules
|
|
|
|
- Test pending migrations on a dump of the current production DB, not on fixtures.
|
|
- Use a local MariaDB Docker container matching the production version and collation.
|
|
- Execute each migration file as one DB session so session variables such as `SET FOREIGN_KEY_CHECKS = 0` remain in effect for the whole file.
|
|
- If migrations fail in Docker, fix them before touching production.
|
|
|
|
## Common Pitfalls
|
|
|
|
- Do not use tools that naively split SQL on bare `;`. String literals may contain semicolons.
|
|
- `SET FOREIGN_KEY_CHECKS = 0` is session-scoped. If the file is split across multiple sessions, FK checks come back on.
|
|
- When adding a new FK to legacy data, repair missing parent rows before enforcing the constraint unless data loss is explicitly acceptable.
|