diff --git a/internal/database/db.go b/internal/database/db.go index b82294add..2e5c99f83 100644 --- a/internal/database/db.go +++ b/internal/database/db.go @@ -2698,6 +2698,11 @@ func InitDB(dbPath string) error { } c := &gorm.Config{Logger: gormLogger, DisableForeignKeyConstraintWhenMigrating: true} + // Reopening replaces the process pool; the replaced one would keep its file open. + if err := CloseDB(); err != nil { + log.Printf("close the replaced database pool: %v", err) + } + var err error switch config.GetDBKind() { case "postgres": diff --git a/internal/database/db_reopen_test.go b/internal/database/db_reopen_test.go new file mode 100644 index 000000000..c3c856161 --- /dev/null +++ b/internal/database/db_reopen_test.go @@ -0,0 +1,27 @@ +package database + +import ( + "path/filepath" + "testing" +) + +// A replaced pool that stays open keeps its database file open; Windows then +// cannot delete or replace that file. +func TestInitDBClosesThePoolItReplaces(t *testing.T) { + dbPath := filepath.Join(t.TempDir(), "x-ui.db") + if err := InitDB(dbPath); err != nil { + t.Fatalf("first InitDB: %v", err) + } + t.Cleanup(func() { _ = CloseDB() }) + replaced, err := GetDB().DB() + if err != nil { + t.Fatalf("first pool: %v", err) + } + + if err := InitDB(dbPath); err != nil { + t.Fatalf("second InitDB: %v", err) + } + if err := replaced.Ping(); err == nil || err.Error() != "sql: database is closed" { + t.Fatalf("replaced pool Ping() = %v, want sql: database is closed", err) + } +}