store: add users table and user_id scoping to migrations

Schema-only step toward per-user profile isolation: profile/workout_kinds/
sync_state are rebuilt to drop their singleton constraints, activities/
sync_runs gain a nullable user_id column. Store methods are scoped in
later tasks.
This commit is contained in:
2026-07-25 10:20:59 +02:00
parent 033e412cb7
commit 245d4bf821
7 changed files with 201 additions and 1 deletions

View File

@@ -24,7 +24,10 @@ type DB struct {
// Open opens (creating if needed) the SQLite database at path and applies
// any migrations that haven't run yet.
func Open(path string) (*DB, error) {
sqlDB, err := sql.Open("sqlite", path+"?_pragma=foreign_keys(1)")
// Don't enable foreign keys in connection string; instead, enable them
// after migrations complete. This allows migrations to disable FK checks
// as needed for table rebuilds.
sqlDB, err := sql.Open("sqlite", path)
if err != nil {
return nil, fmt.Errorf("open sqlite database: %w", err)
}
@@ -82,10 +85,23 @@ func (db *DB) migrate() error {
if err != nil {
return fmt.Errorf("begin migration tx for %s: %w", name, err)
}
// Disable foreign key enforcement for the migration so that table
// rebuilds (e.g. dropping a table with dependent foreign keys) can
// succeed. The migrations themselves use PRAGMA defer_foreign_keys
// when possible; this is a fallback for modernc.org/sqlite's
// limitations with that pragma in transactions.
if _, err := tx.Exec(`PRAGMA foreign_keys = OFF`); err != nil {
tx.Rollback()
return fmt.Errorf("disable foreign keys for migration %s: %w", name, err)
}
if _, err := tx.Exec(string(content)); err != nil {
tx.Rollback()
return fmt.Errorf("apply migration %s: %w", name, err)
}
if _, err := tx.Exec(`PRAGMA foreign_keys = ON`); err != nil {
tx.Rollback()
return fmt.Errorf("enable foreign keys after migration %s: %w", name, err)
}
if _, err := tx.Exec(`INSERT INTO schema_migrations (filename) VALUES (?)`, name); err != nil {
tx.Rollback()
return fmt.Errorf("record migration %s: %w", name, err)
@@ -94,5 +110,9 @@ func (db *DB) migrate() error {
return fmt.Errorf("commit migration %s: %w", name, err)
}
}
// Enable foreign key enforcement after all migrations are complete.
if _, err := db.Exec(`PRAGMA foreign_keys = ON`); err != nil {
return fmt.Errorf("enable foreign keys after migrations: %w", err)
}
return nil
}