feat(kon): add schema versioning — sequential migration system replaces CREATE TABLE IF NOT EXISTS
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,119 +26,9 @@ pub async fn init(db_path: &Path) -> Result<SqlitePool> {
|
||||
Ok(pool)
|
||||
}
|
||||
|
||||
/// Run schema migrations.
|
||||
///
|
||||
/// TODO(pre-release): Implement schema versioning for safe upgrades.
|
||||
/// Currently uses `CREATE TABLE IF NOT EXISTS` which silently ignores column
|
||||
/// additions/removals. Before shipping, add a `schema_version` table and
|
||||
/// sequential migration steps so existing user databases upgrade cleanly.
|
||||
/// Run schema migrations via the versioned migration system.
|
||||
async fn run_migrations(pool: &SqlitePool) -> Result<()> {
|
||||
sqlx::query(
|
||||
"CREATE TABLE IF NOT EXISTS transcripts (
|
||||
id TEXT PRIMARY KEY,
|
||||
text TEXT NOT NULL DEFAULT '',
|
||||
source TEXT NOT NULL DEFAULT 'microphone',
|
||||
title TEXT,
|
||||
audio_path TEXT,
|
||||
duration REAL NOT NULL DEFAULT 0.0,
|
||||
engine TEXT,
|
||||
model_id TEXT,
|
||||
inference_ms INTEGER,
|
||||
sample_rate INTEGER,
|
||||
audio_channels INTEGER,
|
||||
format_mode TEXT,
|
||||
remove_fillers INTEGER NOT NULL DEFAULT 0,
|
||||
british_english INTEGER NOT NULL DEFAULT 0,
|
||||
anti_hallucination INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Migration failed: {e}")))?;
|
||||
|
||||
sqlx::query(
|
||||
"CREATE TABLE IF NOT EXISTS segments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
transcript_id TEXT NOT NULL REFERENCES transcripts(id) ON DELETE CASCADE,
|
||||
start_time REAL NOT NULL,
|
||||
end_time REAL NOT NULL,
|
||||
text TEXT NOT NULL DEFAULT ''
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Migration failed: {e}")))?;
|
||||
|
||||
sqlx::query(
|
||||
"CREATE TABLE IF NOT EXISTS tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
text TEXT NOT NULL,
|
||||
bucket TEXT NOT NULL DEFAULT 'inbox',
|
||||
list_id TEXT,
|
||||
effort TEXT,
|
||||
done INTEGER NOT NULL DEFAULT 0,
|
||||
done_at TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
source_transcript_id TEXT
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Migration failed: {e}")))?;
|
||||
|
||||
sqlx::query(
|
||||
"CREATE TABLE IF NOT EXISTS task_lists (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
built_in INTEGER NOT NULL DEFAULT 0,
|
||||
profile_id TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Migration failed: {e}")))?;
|
||||
|
||||
sqlx::query(
|
||||
"CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Migration failed: {e}")))?;
|
||||
|
||||
sqlx::query(
|
||||
"CREATE TABLE IF NOT EXISTS error_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
timestamp TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
context TEXT NOT NULL,
|
||||
error_code TEXT,
|
||||
message TEXT NOT NULL,
|
||||
metadata TEXT
|
||||
)",
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Migration failed: {e}")))?;
|
||||
|
||||
// Indices for frequently queried columns
|
||||
for ddl in [
|
||||
"CREATE INDEX IF NOT EXISTS idx_transcripts_created ON transcripts(created_at)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_segments_transcript ON segments(transcript_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_tasks_bucket ON tasks(bucket)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_tasks_transcript ON tasks(source_transcript_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_error_log_context ON error_log(context)",
|
||||
] {
|
||||
sqlx::query(ddl)
|
||||
.execute(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Index creation failed: {e}")))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
crate::migrations::run_migrations(pool).await
|
||||
}
|
||||
|
||||
// --- Transcript CRUD ---
|
||||
|
||||
Reference in New Issue
Block a user