65 lines
1.5 KiB
Go
65 lines
1.5 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
// DB wraps the SQL database connection.
|
|
type DB struct {
|
|
*sql.DB
|
|
}
|
|
|
|
// Open opens a SQLite database and runs migrations.
|
|
func Open(path string) (*DB, error) {
|
|
sqlDB, err := sql.Open("sqlite3", path+"?_journal_mode=WAL&_foreign_keys=on")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("opening database: %w", err)
|
|
}
|
|
|
|
if err := sqlDB.Ping(); err != nil {
|
|
return nil, fmt.Errorf("pinging database: %w", err)
|
|
}
|
|
|
|
db := &DB{sqlDB}
|
|
|
|
if err := db.migrate(); err != nil {
|
|
return nil, fmt.Errorf("running migrations: %w", err)
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
func (db *DB) migrate() error {
|
|
// Ensure schema_migrations table exists
|
|
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
version INTEGER PRIMARY KEY,
|
|
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
)`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get current version
|
|
var currentVersion int
|
|
row := db.QueryRow("SELECT COALESCE(MAX(version), 0) FROM schema_migrations")
|
|
if err := row.Scan(¤tVersion); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Apply new migrations
|
|
for i := currentVersion; i < len(migrations); i++ {
|
|
log.Printf("Applying migration %d...", i+1)
|
|
if _, err := db.Exec(migrations[i]); err != nil {
|
|
return fmt.Errorf("migration %d: %w", i+1, err)
|
|
}
|
|
if _, err := db.Exec("INSERT INTO schema_migrations (version) VALUES (?)", i+1); err != nil {
|
|
return fmt.Errorf("recording migration %d: %w", i+1, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|