Goal
Replace the raw DDL in the async SQLite and MySQL drivers with sqlx::migrate!(), making schema evolution explicit, versioned, and aligned across all SQL backends.
Why
After subissue 1525-05 the drivers still own their schema through hand-written CREATE TABLE IF NOT EXISTS statements. That has no history, no ordering guarantees, and no safe way to apply incremental schema changes. sqlx::migrate!() provides versioned SQL files, automatic up-migration on startup, and a _sqlx_migrations tracking table — a foundation required before PostgreSQL can be added (1525-08).
Spec
The detailed implementation spec is the source of truth:
docs/issues/1525-06-introduce-schema-migrations.md
Summary
- Verify the three existing migration files under
packages/tracker-core/migrations/{sqlite,mysql}/ match the schema produced by 1525-05.
- Enable
sqlx's macros feature; declare a static MIGRATOR: Migrator = sqlx::migrate!("migrations/<backend>") in each driver; add Error::migration_error() wrapping sqlx::migrate::MigrateError.
- Add a
bootstrap_legacy_schema() helper to handle pre-v4 databases without _sqlx_migrations (fake-apply the three pre-existing migrations after verifying preconditions).
- Wire
create_database_tables() to call bootstrap_legacy_schema() then MIGRATOR.run(). Fix drop_database_tables() to also drop _sqlx_migrations and torrent_aggregate_metrics (all five drops use DROP TABLE IF EXISTS).
- Update
packages/tracker-core/migrations/README.md to document automatic migration, file immutability, and the v4 upgrade prerequisite.
- Add tests for fresh-database, idempotency, drop/create cycle, legacy bootstrap, and partial-migration guard scenarios.
History-alignment pattern
All backends share the same set of migration filenames/timestamps. Migrations that are no-ops for a backend remain as comment-only files. PostgreSQL (1525-08) starts from migration 1, not a catch-up.
Acceptance Criteria
Parent EPIC
#1525
Goal
Replace the raw DDL in the async SQLite and MySQL drivers with
sqlx::migrate!(), making schema evolution explicit, versioned, and aligned across all SQL backends.Why
After subissue
1525-05the drivers still own their schema through hand-writtenCREATE TABLE IF NOT EXISTSstatements. That has no history, no ordering guarantees, and no safe way to apply incremental schema changes.sqlx::migrate!()provides versioned SQL files, automatic up-migration on startup, and a_sqlx_migrationstracking table — a foundation required before PostgreSQL can be added (1525-08).Spec
The detailed implementation spec is the source of truth:
docs/issues/1525-06-introduce-schema-migrations.mdSummary
packages/tracker-core/migrations/{sqlite,mysql}/match the schema produced by1525-05.sqlx'smacrosfeature; declare astatic MIGRATOR: Migrator = sqlx::migrate!("migrations/<backend>")in each driver; addError::migration_error()wrappingsqlx::migrate::MigrateError.bootstrap_legacy_schema()helper to handle pre-v4 databases without_sqlx_migrations(fake-apply the three pre-existing migrations after verifying preconditions).create_database_tables()to callbootstrap_legacy_schema()thenMIGRATOR.run(). Fixdrop_database_tables()to also drop_sqlx_migrationsandtorrent_aggregate_metrics(all five drops useDROP TABLE IF EXISTS).packages/tracker-core/migrations/README.mdto document automatic migration, file immutability, and the v4 upgrade prerequisite.History-alignment pattern
All backends share the same set of migration filenames/timestamps. Migrations that are no-ops for a backend remain as comment-only files. PostgreSQL (
1525-08) starts from migration 1, not a catch-up.Acceptance Criteria
sqlx::migrate!()is used in both drivers; no raw DDL remains increate_database_tables().bootstrap_legacy_schema()verifies migrations 2 and 3 were applied before fake-applying, returning a descriptive error otherwise.drop_database_tables()drops all five tables (including_sqlx_migrationsandtorrent_aggregate_metrics) usingDROP TABLE IF EXISTS.Error::migration_error()wrapssqlx::migrate::MigrateError.packages/tracker-core/migrations/README.mddocuments the new automatic migration behavior and v4 upgrade requirement.cargo test --workspace --all-targetspasses.linter allexits with code0.Parent EPIC
#1525