Files
jukebox_maker/internal/db/db.go

132 lines
2.8 KiB
Go

package db
import (
"database/sql"
"time"
_ "modernc.org/sqlite"
)
type DB struct {
sql *sql.DB
}
type CopyRecord struct {
DiskID string
SourcePath string
FileSize int64
CopiedAt time.Time
}
func Open(path string) (*DB, error) {
conn, err := sql.Open("sqlite", path+"?_journal=WAL&_timeout=5000")
if err != nil {
return nil, err
}
conn.SetMaxOpenConns(1)
d := &DB{sql: conn}
if err := d.migrate(); err != nil {
conn.Close()
return nil, err
}
return d, nil
}
func (d *DB) Close() error {
return d.sql.Close()
}
func (d *DB) migrate() error {
_, err := d.sql.Exec(`
CREATE TABLE IF NOT EXISTS copy_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
disk_id TEXT NOT NULL,
source_path TEXT NOT NULL,
file_size INTEGER NOT NULL DEFAULT 0,
copied_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_copy_history_disk_path
ON copy_history (disk_id, source_path);
CREATE TABLE IF NOT EXISTS disk_stats (
disk_id TEXT PRIMARY KEY,
last_copied_at DATETIME NOT NULL
);
`)
return err
}
func (d *DB) WasCopied(diskID, sourcePath string) (bool, error) {
var n int
err := d.sql.QueryRow(
`SELECT COUNT(*) FROM copy_history WHERE disk_id=? AND source_path=?`,
diskID, sourcePath,
).Scan(&n)
return n > 0, err
}
func (d *DB) RecordCopy(rec CopyRecord) error {
t := rec.CopiedAt
if t.IsZero() {
t = time.Now().UTC()
}
tx, err := d.sql.Begin()
if err != nil {
return err
}
defer tx.Rollback()
if _, err := tx.Exec(
`INSERT OR IGNORE INTO copy_history (disk_id, source_path, file_size, copied_at) VALUES (?,?,?,?)`,
rec.DiskID, rec.SourcePath, rec.FileSize, t.Format(time.RFC3339),
); err != nil {
return err
}
if _, err := tx.Exec(
`INSERT INTO disk_stats (disk_id, last_copied_at) VALUES (?, ?)
ON CONFLICT(disk_id) DO UPDATE SET last_copied_at=excluded.last_copied_at`,
rec.DiskID, t.Format(time.RFC3339),
); err != nil {
return err
}
return tx.Commit()
}
func (d *DB) CopiedPaths(diskID string) (map[string]struct{}, error) {
rows, err := d.sql.Query(
`SELECT source_path FROM copy_history WHERE disk_id=?`, diskID,
)
if err != nil {
return nil, err
}
defer rows.Close()
m := make(map[string]struct{})
for rows.Next() {
var p string
if err := rows.Scan(&p); err != nil {
return nil, err
}
m[p] = struct{}{}
}
return m, rows.Err()
}
func (d *DB) LastCopiedAt(diskID string) (time.Time, bool, error) {
var raw string
err := d.sql.QueryRow(
`SELECT last_copied_at FROM disk_stats WHERE disk_id=?`,
diskID,
).Scan(&raw)
if err == sql.ErrNoRows {
return time.Time{}, false, nil
}
if err != nil {
return time.Time{}, false, err
}
t, err := time.Parse(time.RFC3339, raw)
if err != nil {
return time.Time{}, false, err
}
return t, true, nil
}