diff options
| author | Ben Sima <ben@bensima.com> | 2025-11-27 13:44:47 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bensima.com> | 2025-11-27 13:44:47 -0500 |
| commit | e47da20db9dd5f5d0a09ffcf2b17ad4e1d78115b (patch) | |
| tree | 887257df85237985f07a799f8f6001a06e371e3c /Omni/Task/Core.hs | |
| parent | 1d8553c5ffd7f2a063078840886acb24153e9854 (diff) | |
Implement proper schema migrations for ALTER TABLE
All tests pass. The implementation adds:
1. **`runMigrations`** - Called at the end of `initTaskDb` to run
migrat 2. **`migrateTable`** - Compares expected columns against
existing colum 3. **`getTableColumns`** - Uses `PRAGMA table_info` to
get existing colu 4. **`addColumn`** - Runs `ALTER TABLE ADD COLUMN`
for missing columns 5. **Column definitions** - Lists of expected
columns for `task_activity
When new columns are added to the schema in the future, you just
add the
Task-Id: t-152.3
Diffstat (limited to 'Omni/Task/Core.hs')
| -rw-r--r-- | Omni/Task/Core.hs | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/Omni/Task/Core.hs b/Omni/Task/Core.hs index ffecd60..5af4ce4 100644 --- a/Omni/Task/Core.hs +++ b/Omni/Task/Core.hs @@ -374,6 +374,75 @@ initTaskDb = do \ tokens_used INTEGER, \ \ FOREIGN KEY (task_id) REFERENCES tasks(id) \ \)" + runMigrations conn + +-- | Run schema migrations to add missing columns to existing tables +runMigrations :: SQL.Connection -> IO () +runMigrations conn = do + migrateTable conn "task_activity" taskActivityColumns + migrateTable conn "tasks" tasksColumns + migrateTable conn "retry_context" retryContextColumns + +-- | Expected columns for task_activity table (name, type, nullable) +taskActivityColumns :: [(Text, Text)] +taskActivityColumns = + [ ("id", "INTEGER"), + ("task_id", "TEXT"), + ("timestamp", "DATETIME"), + ("stage", "TEXT"), + ("message", "TEXT"), + ("metadata", "TEXT"), + ("amp_thread_url", "TEXT"), + ("started_at", "DATETIME"), + ("completed_at", "DATETIME"), + ("cost_cents", "INTEGER"), + ("tokens_used", "INTEGER") + ] + +-- | Expected columns for tasks table +tasksColumns :: [(Text, Text)] +tasksColumns = + [ ("id", "TEXT"), + ("title", "TEXT"), + ("type", "TEXT"), + ("parent", "TEXT"), + ("namespace", "TEXT"), + ("status", "TEXT"), + ("priority", "TEXT"), + ("dependencies", "TEXT"), + ("description", "TEXT"), + ("created_at", "TIMESTAMP"), + ("updated_at", "TIMESTAMP") + ] + +-- | Expected columns for retry_context table +retryContextColumns :: [(Text, Text)] +retryContextColumns = + [ ("task_id", "TEXT"), + ("original_commit", "TEXT"), + ("conflict_files", "TEXT"), + ("attempt", "INTEGER"), + ("reason", "TEXT") + ] + +-- | Migrate a table by adding any missing columns +migrateTable :: SQL.Connection -> Text -> [(Text, Text)] -> IO () +migrateTable conn tableName expectedCols = do + existingCols <- getTableColumns conn tableName + let missingCols = filter (\(name, _) -> name `notElem` existingCols) expectedCols + traverse_ (addColumn conn tableName) missingCols + +-- | Get list of column names for a table using PRAGMA table_info +getTableColumns :: SQL.Connection -> Text -> IO [Text] +getTableColumns conn tableName = do + rows <- SQL.query conn "PRAGMA table_info(?)" (SQL.Only tableName) :: IO [(Int, Text, Text, Int, Maybe Text, Int)] + pure [colName | (_, colName, _, _, _, _) <- rows] + +-- | Add a column to a table +addColumn :: SQL.Connection -> Text -> (Text, Text) -> IO () +addColumn conn tableName (colName, colType) = do + let sql = "ALTER TABLE " <> tableName <> " ADD COLUMN " <> colName <> " " <> colType + SQL.execute_ conn (SQL.Query sql) -- Generate a sequential task ID (t-1, t-2, t-3, ...) generateId :: IO Text |
