From 0144ad17d9586be108244cecb202211e4fa93a98 Mon Sep 17 00:00:00 2001 From: Arsen Musayelyan Date: Wed, 11 Jan 2023 14:14:31 -0800 Subject: [PATCH] Move database open code into internal/db --- cmd/lure-api-server/db.go | 18 +------- db.go | 18 +------- fix.go | 11 +---- internal/db/db.go | 85 ++++++++++++++++++++++++++++++++++++- internal/db/db_test.go | 31 ++++++-------- internal/repos/find_test.go | 15 +------ internal/repos/pull_test.go | 8 +--- 7 files changed, 106 insertions(+), 80 deletions(-) diff --git a/cmd/lure-api-server/db.go b/cmd/lure-api-server/db.go index 31ce069..000b85b 100644 --- a/cmd/lure-api-server/db.go +++ b/cmd/lure-api-server/db.go @@ -1,8 +1,6 @@ package main import ( - "os" - "github.com/jmoiron/sqlx" "go.arsenm.dev/logger/log" "go.arsenm.dev/lure/internal/config" @@ -12,21 +10,9 @@ import ( var gdb *sqlx.DB func init() { - fi, err := os.Stat(config.DBPath) - if err == nil { - // TODO: This should be removed by the first stable release. - if fi.IsDir() { - log.Fatal("Your package cache database is using the old database engine. Please remove ~/.cache/lure and then run `lure ref`.").Send() - } - } - - gdb, err = sqlx.Open("sqlite", config.DBPath) + var err error + gdb, err = db.Open(config.DBPath) if err != nil { log.Fatal("Error opening database").Err(err).Send() } - - err = db.Init(gdb) - if err != nil { - log.Fatal("Error initializing database").Err(err).Send() - } } diff --git a/db.go b/db.go index 31ce069..000b85b 100644 --- a/db.go +++ b/db.go @@ -1,8 +1,6 @@ package main import ( - "os" - "github.com/jmoiron/sqlx" "go.arsenm.dev/logger/log" "go.arsenm.dev/lure/internal/config" @@ -12,21 +10,9 @@ import ( var gdb *sqlx.DB func init() { - fi, err := os.Stat(config.DBPath) - if err == nil { - // TODO: This should be removed by the first stable release. - if fi.IsDir() { - log.Fatal("Your package cache database is using the old database engine. Please remove ~/.cache/lure and then run `lure ref`.").Send() - } - } - - gdb, err = sqlx.Open("sqlite", config.DBPath) + var err error + gdb, err = db.Open(config.DBPath) if err != nil { log.Fatal("Error opening database").Err(err).Send() } - - err = db.Init(gdb) - if err != nil { - log.Fatal("Error initializing database").Err(err).Send() - } } diff --git a/fix.go b/fix.go index cdff945..1b9016b 100644 --- a/fix.go +++ b/fix.go @@ -3,7 +3,6 @@ package main import ( "os" - "github.com/jmoiron/sqlx" "github.com/urfave/cli/v2" "go.arsenm.dev/logger/log" "go.arsenm.dev/lure/internal/config" @@ -28,18 +27,12 @@ func fixCmd(c *cli.Context) error { log.Fatal("Unable to create new cache directory").Err(err).Send() } - gdb, err = sqlx.Open("sqlite", config.DBPath) - if err != nil { - log.Fatal("Unable to create new database").Err(err).Send() - } - // Make sure the DB is rebuilt when repos are pulled - config.DBPresent = false - - err = db.Init(gdb) + gdb, err = db.Open(config.DBPath) if err != nil { log.Fatal("Error initializing database").Err(err).Send() } + config.DBPresent = false err = repos.Pull(c.Context, gdb, cfg.Repos) if err != nil { diff --git a/internal/db/db.go b/internal/db/db.go index 8ce474e..991f6ff 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -6,12 +6,17 @@ import ( "encoding/json" "errors" "fmt" + "os" "github.com/jmoiron/sqlx" + "go.arsenm.dev/logger/log" + "go.arsenm.dev/lure/internal/config" "golang.org/x/exp/slices" "modernc.org/sqlite" ) +const CurrentVersion = 1 + func init() { sqlite.MustRegisterScalarFunction("json_array_contains", 2, JsonArrayContains) } @@ -35,8 +40,42 @@ type Package struct { Repository string `db:"repository"` } +type version struct { + Version int `db:"version"` +} + +func Open(dsn string) (*sqlx.DB, error) { + if dsn != ":memory:" { + fi, err := os.Stat(config.DBPath) + if err == nil { + // TODO: This should be removed by the first stable release. + if fi.IsDir() { + log.Warn("Your database is using the old database engine; rebuilding").Send() + + err = os.RemoveAll(config.DBPath) + if err != nil { + log.Fatal("Error removing old database").Err(err).Send() + } + config.DBPresent = false + } + } + } + + db, err := sqlx.Open("sqlite", dsn) + if err != nil { + log.Fatal("Error opening database").Err(err).Send() + } + + err = Init(db, dsn) + if err != nil { + log.Fatal("Error initializing database").Err(err).Send() + } + + return db, nil +} + // Init initializes the database -func Init(db *sqlx.DB) error { +func Init(db *sqlx.DB, dsn string) error { *db = *db.Unsafe() _, err := db.Exec(` CREATE TABLE IF NOT EXISTS pkgs ( @@ -57,7 +96,51 @@ func Init(db *sqlx.DB) error { builddepends TEXT CHECK(builddepends = 'null' OR (JSON_VALID(builddepends) AND JSON_TYPE(builddepends) = 'object')), UNIQUE(name, repository) ); + + CREATE TABLE IF NOT EXISTS lure_db_version ( + version INT NOT NULL + ); `) + if err != nil { + return err + } + + ver, ok := GetVersion(db) + if !ok { + return addVersion(db, CurrentVersion) + } + + if ver != CurrentVersion { + log.Warn("Database version mismatch; rebuilding").Int("version", ver).Int("expected", CurrentVersion).Send() + + db.Close() + err = os.Remove(config.DBPath) + if err != nil { + return err + } + config.DBPresent = false + + tdb, err := Open(dsn) + if err != nil { + return err + } + *db = *tdb + } + + return nil +} + +func GetVersion(db *sqlx.DB) (int, bool) { + var ver version + err := db.Get(&ver, "SELECT * FROM lure_db_version LIMIT 1;") + if err != nil { + return 0, false + } + return ver.Version, true +} + +func addVersion(db *sqlx.DB, ver int) error { + _, err := db.Exec(`INSERT INTO lure_db_version(version) VALUES (?);`, ver) return err } diff --git a/internal/db/db_test.go b/internal/db/db_test.go index fc5bf0f..80ece7b 100644 --- a/internal/db/db_test.go +++ b/internal/db/db_test.go @@ -32,18 +32,6 @@ var testPkg = db.Package{ Repository: "default", } -func getDB(t *testing.T) (*sqlx.DB, error) { - t.Helper() - - gdb, err := sqlx.Open("sqlite", ":memory:") - if err != nil { - return nil, err - } - - err = db.Init(gdb) - return gdb, err -} - func TestInit(t *testing.T) { gdb, err := sqlx.Open("sqlite", ":memory:") if err != nil { @@ -51,7 +39,7 @@ func TestInit(t *testing.T) { } defer gdb.Close() - err = db.Init(gdb) + err = db.Init(gdb, ":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } @@ -60,10 +48,17 @@ func TestInit(t *testing.T) { if err != nil { t.Fatalf("Expected no error, got %s", err) } + + ver, ok := db.GetVersion(gdb) + if !ok { + t.Errorf("Expected version to be present") + } else if ver != db.CurrentVersion { + t.Errorf("Expected version %d, got %d", db.CurrentVersion, ver) + } } func TestInsertPackage(t *testing.T) { - gdb, err := getDB(t) + gdb, err := db.Open(":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } @@ -86,7 +81,7 @@ func TestInsertPackage(t *testing.T) { } func TestGetPkgs(t *testing.T) { - gdb, err := getDB(t) + gdb, err := db.Open(":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } @@ -126,7 +121,7 @@ func TestGetPkgs(t *testing.T) { } func TestGetPkg(t *testing.T) { - gdb, err := getDB(t) + gdb, err := db.Open(":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } @@ -162,7 +157,7 @@ func TestGetPkg(t *testing.T) { } func TestDeletePkgs(t *testing.T) { - gdb, err := getDB(t) + gdb, err := db.Open(":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } @@ -200,7 +195,7 @@ func TestDeletePkgs(t *testing.T) { } func TestJsonArrayContains(t *testing.T) { - gdb, err := getDB(t) + gdb, err := db.Open(":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } diff --git a/internal/repos/find_test.go b/internal/repos/find_test.go index 2d30123..761e26f 100644 --- a/internal/repos/find_test.go +++ b/internal/repos/find_test.go @@ -6,24 +6,18 @@ import ( "strings" "testing" - "github.com/jmoiron/sqlx" "go.arsenm.dev/lure/internal/db" "go.arsenm.dev/lure/internal/repos" "go.arsenm.dev/lure/internal/types" ) func TestFindPkgs(t *testing.T) { - gdb, err := sqlx.Open("sqlite", ":memory:") + gdb, err := db.Open(":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } defer gdb.Close() - err = db.Init(gdb) - if err != nil { - t.Fatalf("Expected no error, got %s", err) - } - setCfgDirs(t) defer removeCacheDir(t) @@ -69,17 +63,12 @@ func TestFindPkgs(t *testing.T) { } func TestFindPkgsEmpty(t *testing.T) { - gdb, err := sqlx.Open("sqlite", ":memory:") + gdb, err := db.Open(":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } defer gdb.Close() - err = db.Init(gdb) - if err != nil { - t.Fatalf("Expected no error, got %s", err) - } - setCfgDirs(t) defer removeCacheDir(t) diff --git a/internal/repos/pull_test.go b/internal/repos/pull_test.go index e8a2d4a..8b243c1 100644 --- a/internal/repos/pull_test.go +++ b/internal/repos/pull_test.go @@ -6,7 +6,6 @@ import ( "path/filepath" "testing" - "github.com/jmoiron/sqlx" "go.arsenm.dev/lure/internal/config" "go.arsenm.dev/lure/internal/db" "go.arsenm.dev/lure/internal/repos" @@ -48,17 +47,12 @@ func removeCacheDir(t *testing.T) { } func TestPull(t *testing.T) { - gdb, err := sqlx.Open("sqlite", ":memory:") + gdb, err := db.Open(":memory:") if err != nil { t.Fatalf("Expected no error, got %s", err) } defer gdb.Close() - err = db.Init(gdb) - if err != nil { - t.Fatalf("Expected no error, got %s", err) - } - setCfgDirs(t) defer removeCacheDir(t)