save current changes
This commit is contained in:
68
lib/db/migrator.ts
Normal file
68
lib/db/migrator.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { SqliteDatabase } from './sqlite';
|
||||
|
||||
/**
|
||||
* SQLファイルベースのMigration機構。
|
||||
* - Migrationファイルをファイル名順に実行する
|
||||
* - 実行済みファイルは再実行しない
|
||||
* - 1ファイルごとにトランザクションを張り、失敗時はロールバックする
|
||||
*/
|
||||
export class Migrator {
|
||||
constructor(
|
||||
private readonly db: SqliteDatabase,
|
||||
private readonly migrationsDir: string
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 未適用のMigrationをファイル名順に実行する
|
||||
*/
|
||||
migrate(): void {
|
||||
this.ensureMigrationsTable();
|
||||
|
||||
const applied = this.db.query<{ filename: string }>(
|
||||
'SELECT filename FROM schema_migrations'
|
||||
);
|
||||
const appliedSet = new Set(applied.map((x) => x.filename));
|
||||
|
||||
const files = fs
|
||||
.readdirSync(this.migrationsDir)
|
||||
.filter((file) => file.endsWith('.sql'))
|
||||
.sort();
|
||||
|
||||
for (const file of files) {
|
||||
if (appliedSet.has(file)) continue;
|
||||
|
||||
const fullPath = path.join(this.migrationsDir, file);
|
||||
const sql = fs.readFileSync(fullPath, 'utf-8');
|
||||
|
||||
this.db.transaction(() => {
|
||||
this.db.execute(sql);
|
||||
this.db.execute(
|
||||
`INSERT INTO schema_migrations (filename, applied_at) VALUES (@filename, @appliedAt)`,
|
||||
{ filename: file, appliedAt: new Date().toISOString() }
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 適用済みMigration一覧を取得する
|
||||
*/
|
||||
getAppliedMigrations(): { filename: string; applied_at: string }[] {
|
||||
this.ensureMigrationsTable();
|
||||
return this.db.query<{ filename: string; applied_at: string }>(
|
||||
'SELECT filename, applied_at FROM schema_migrations ORDER BY filename'
|
||||
);
|
||||
}
|
||||
|
||||
private ensureMigrationsTable(): void {
|
||||
this.db.execute(`
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
filename TEXT NOT NULL UNIQUE,
|
||||
applied_at TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user