store: activities UNIQUE constraint rebuild for per-user design

Migration 0026 rebuilds the activities table to enforce UNIQUE(user_id,
garmin_activity_id) as the composite primary constraint, removing the old
global UNIQUE(garmin_activity_id) constraint from migration 0001. This
allows the same Garmin activity ID to appear for different users without
conflicts, which is essential for the per-user profile design and enables
the TestUpsertActivity_SameGarminActivityIDAllowedAcrossDifferentUsers
test to pass.
This commit is contained in:
2026-07-25 14:12:54 +02:00
parent 62753e4604
commit d8b722820d

View File

@@ -0,0 +1,46 @@
-- activities.garmin_activity_id's UNIQUE constraint (migration 0001) must become
-- per-user (UNIQUE(user_id, garmin_activity_id)) now that the per-user-profile
-- design allows multiple users to share the same Garmin activity ID. The old
-- constraint alone was intentionally left in place by migration 0022 as a
-- belts-and-suspenders safeguard (Garmin IDs are globally unique in practice),
-- but it must be removed now to allow Task 6's cross-user test to pass.
PRAGMA foreign_keys = OFF;
CREATE TABLE activities_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER REFERENCES users(id),
garmin_activity_id INTEGER NOT NULL,
event_type_key TEXT NOT NULL,
workout_id INTEGER,
start_time_utc TEXT NOT NULL,
duration_seconds REAL NOT NULL,
distance_meters REAL NOT NULL,
avg_hr REAL,
max_hr REAL,
avg_speed_mps REAL,
elevation_gain_m REAL,
aerobic_training_effect REAL,
anaerobic_training_effect REAL,
vo2max_value REAL,
raw_json TEXT NOT NULL,
details_fetched_at TEXT,
details_raw_json TEXT,
splits_fetched_at TEXT,
workout_raw_json TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(user_id, garmin_activity_id)
);
INSERT INTO activities_new (id, user_id, garmin_activity_id, event_type_key, workout_id, start_time_utc, duration_seconds, distance_meters, avg_hr, max_hr, avg_speed_mps, elevation_gain_m, aerobic_training_effect, anaerobic_training_effect, vo2max_value, raw_json, details_fetched_at, details_raw_json, splits_fetched_at, workout_raw_json, created_at, updated_at)
SELECT id, user_id, garmin_activity_id, event_type_key, workout_id, start_time_utc, duration_seconds, distance_meters, avg_hr, max_hr, avg_speed_mps, elevation_gain_m, aerobic_training_effect, anaerobic_training_effect, vo2max_value, raw_json, details_fetched_at, details_raw_json, splits_fetched_at, workout_raw_json, created_at, updated_at
FROM activities;
DROP TABLE activities;
ALTER TABLE activities_new RENAME TO activities;
CREATE INDEX idx_activities_start_time ON activities(start_time_utc);
CREATE INDEX idx_activities_user_garmin_id ON activities(user_id, garmin_activity_id);
PRAGMA foreign_keys = ON;