Skip to content

Commit

Permalink
Fixes error if database file doesn't exists. (#12)
Browse files Browse the repository at this point in the history
if db file doesn't exists don't try migration
  • Loading branch information
SaintWish authored Jul 8, 2022
1 parent 29332a3 commit 15d666a
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 171 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ runtime/game/*
dev/*
*.log
*.db
*.bak
345 changes: 174 additions & 171 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"runtime"
"strconv"
"time"
"errors"

entd "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/schema"
Expand Down Expand Up @@ -105,15 +106,6 @@ func main() {

//Connect database.
log.Log.Println("Connecting to database")
// client, err = ent.Open("sqlite3", system.Config.Core.DBString)
// if err != nil {
// log.Log.Fatalf("failed to open connection to sqlite3: %v", err)
// }
// if err := client.Schema.Create(ctx, schema.WithAtlas(true)); err != nil {
// log.Log.Fatalf("failed to create schema resources: %v", err)
// }
// system.Client = client
// defer system.Client.Close()
tmpMigration()
defer system.Client.Close()

Expand Down Expand Up @@ -213,32 +205,132 @@ func tmpMigration() {
oldDbFileName := "./runtime/old_chars.db"
dbBakFileName := dbFileName + ".bak"
dbBakConnStr := "file:" + oldDbFileName + "?cache=shared&mode=rwc&_fk=1"

err := func() error {
/////////////////////////////////////////////////
/////////// Check if migration is required
// 1. Check if the 'players' table exists
sqlClient, err := entd.Open("sqlite3", dbFileName)
if err != nil {
log.Log.Errorf("failed to open connection to sqlite3: %v", err)
return err
}

rows, err := sqlClient.DB().Query("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='players';")

//if file doesn't exists then no migration is needed.
if _, ferr := os.Stat(dbFileName); errors.Is(ferr, os.ErrNotExist) {
client, err := ent.Open("sqlite3", system.Config.Core.DBString)
if err != nil {
log.Log.Errorf("failed to get table: %v", err)
return err
log.Log.Fatalf("failed to open connection to sqlite3: %v", err)
}

var count int64
for rows.Next() {
rows.Scan(&count)
if err := client.Schema.Create(ctx, schema.WithAtlas(true)); err != nil {
log.Log.Fatalf("failed to create schema resources: %v", err)
}
if count > 0 {
log.Log.Println("DB already migrated")

// Set the connection as normal
client, err := ent.Open("sqlite3", system.Config.Core.DBString)
system.Client = client
} else {
err := func() error {
/////////////////////////////////////////////////
/////////// Check if migration is required
// 1. Check if the 'players' table exists
sqlClient, err := entd.Open("sqlite3", dbFileName)
if err != nil {
log.Log.Errorf("failed to open connection to sqlite3: %v", err)
return err
}

rows, err := sqlClient.DB().Query("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='players';")
if err != nil {
log.Log.Errorf("failed to get table: %v", err)
return err
}

var count int64
for rows.Next() {
rows.Scan(&count)
}
if count > 0 {
log.Log.Println("DB already migrated")

// Set the connection as normal
client, err := ent.Open("sqlite3", system.Config.Core.DBString)
if err != nil {
log.Log.Errorf("failed to open connection to sqlite3: %v", err)
return err
}
if err := client.Schema.Create(ctx, schema.WithAtlas(true)); err != nil {
log.Log.Errorf("failed to create schema resources: %v", err)
return err
}
system.Client = client

return nil
}
sqlClient.Close()

log.Log.Println("DB not migrated")

/////////////////////////////////////////////////
/////////// Prepare DB files
// 1. Copy current DB file (prevents irreversible changes)
// 2. Rename current DB file (prevents conflicts with new DB)
currentDbFile, err := os.Open(dbFileName)
if err != nil {
log.Log.Errorf("failed to open current DB file: %v", err)
return err
}

backupDbFile, err := os.Create(dbBakFileName)
if err != nil {
log.Log.Errorf("failed to create backup DB file: %v", err)
return err
}

_, err = io.Copy(backupDbFile, currentDbFile)
if err != nil {
log.Log.Errorf("failed to backup DB file: %v", err)
return err
}
currentDbFile.Close()
backupDbFile.Close()

if err := os.Rename(dbFileName, oldDbFileName); err != nil {
log.Log.Errorf("failed to rename original DB file: %v", err)
return err
}
log.Log.Println("Backed up DB file")

/////////////////////////////////////////////////
/////////// Prepare DB schema
// 1. Rename 'characters' table (allows the use of ORM)
sqlClient, err = entd.Open("sqlite3", dbBakConnStr)
if err != nil {
log.Log.Errorf("failed to open connection to old sqlite3: %v", err)
return err
}
log.Log.Println("Connected to old DB")

_, err = sqlClient.DB().Exec("ALTER TABLE characters RENAME TO old_characters;")
if err != nil {
log.Log.Errorf("failed to rename old table: %v", err)
return err
}
sqlClient.Close()
log.Log.Println("Renamed table")

/////////////////////////////////////////////////
/////////// Collect old data
// 1. Load all characters into memory using ORM
client, err := ent.Open("sqlite3", dbBakConnStr)
if err != nil {
log.Log.Errorf("failed to open connection to old sqlite3: %v", err)
return err
}
log.Log.Println("Connected to old db")

oldCharacters, err := client.DeprecatedCharacter.Query().All(ctx)
if err != nil {
log.Log.Errorf("failed to get all old characters: %v", err)
return err
}
client.Close()
log.Log.Println("Loaded all characters")

/////////////////////////////////////////////////
/////////// Migrate characters to new DB
// 1. Connect to new DB (creating file in the process)
// 2. Loop through old characters
// 3. Convert old character to new Player + Versioned Character
// 4. Log any conversion errors
client, err = ent.Open("sqlite3", system.Config.Core.DBString)
if err != nil {
log.Log.Errorf("failed to open connection to sqlite3: %v", err)
return err
Expand All @@ -248,150 +340,61 @@ func tmpMigration() {
return err
}
system.Client = client

return nil
}
sqlClient.Close()

log.Log.Println("DB not migrated")

/////////////////////////////////////////////////
/////////// Prepare DB files
// 1. Copy current DB file (prevents irreversible changes)
// 2. Rename current DB file (prevents conflicts with new DB)
currentDbFile, err := os.Open(dbFileName)
if err != nil {
log.Log.Errorf("failed to open current DB file: %v", err)
return err
}

backupDbFile, err := os.Create(dbBakFileName)
if err != nil {
log.Log.Errorf("failed to create backup DB file: %v", err)
return err
}

_, err = io.Copy(backupDbFile, currentDbFile)
if err != nil {
log.Log.Errorf("failed to backup DB file: %v", err)
return err
}
currentDbFile.Close()
backupDbFile.Close()

if err := os.Rename(dbFileName, oldDbFileName); err != nil {
log.Log.Errorf("failed to rename original DB file: %v", err)
return err
}
log.Log.Println("Backed up DB file")

/////////////////////////////////////////////////
/////////// Prepare DB schema
// 1. Rename 'characters' table (allows the use of ORM)
sqlClient, err = entd.Open("sqlite3", dbBakConnStr)
if err != nil {
log.Log.Errorf("failed to open connection to old sqlite3: %v", err)
return err
}
log.Log.Println("Connected to old DB")

_, err = sqlClient.DB().Exec("ALTER TABLE characters RENAME TO old_characters;")
if err != nil {
log.Log.Errorf("failed to rename old table: %v", err)
return err
}
sqlClient.Close()
log.Log.Println("Renamed table")

/////////////////////////////////////////////////
/////////// Collect old data
// 1. Load all characters into memory using ORM
client, err := ent.Open("sqlite3", dbBakConnStr)
if err != nil {
log.Log.Errorf("failed to open connection to old sqlite3: %v", err)
return err
}
log.Log.Println("Connected to old db")

oldCharacters, err := client.DeprecatedCharacter.Query().All(ctx)
if err != nil {
log.Log.Errorf("failed to get all old characters: %v", err)
return err
}
client.Close()
log.Log.Println("Loaded all characters")

/////////////////////////////////////////////////
/////////// Migrate characters to new DB
// 1. Connect to new DB (creating file in the process)
// 2. Loop through old characters
// 3. Convert old character to new Player + Versioned Character
// 4. Log any conversion errors
client, err = ent.Open("sqlite3", system.Config.Core.DBString)
if err != nil {
log.Log.Errorf("failed to open connection to sqlite3: %v", err)
return err
}
if err := client.Schema.Create(ctx, schema.WithAtlas(true)); err != nil {
log.Log.Errorf("failed to create schema resources: %v", err)
return err
}
system.Client = client
log.Log.Println("connected to new db")

errLog, err := os.Create("./runtime/logs/migration_errors.log")
if err != nil {
log.Log.Errorf("failed to create migration error log: %v", err)
return err
}

cLen := len(oldCharacters)
var failed int
for i, c := range oldCharacters {
fmt.Printf("\033[1A\033[Kmigrating %d of %d; steamId='%s'\n", i+1, cLen, c.Steamid)
player, err := client.Player.Query().Where(player.Steamid(c.Steamid)).Only(ctx)
if ent.IsNotFound(err) {
player, err = client.Player.Create().
SetSteamid(c.Steamid).
log.Log.Println("connected to new db")

errLog, err := os.Create("./runtime/logs/migration_errors.log")
if err != nil {
log.Log.Errorf("failed to create migration error log: %v", err)
return err
}

cLen := len(oldCharacters)
var failed int
for i, c := range oldCharacters {
fmt.Printf("\033[1A\033[Kmigrating %d of %d; steamId='%s'\n", i+1, cLen, c.Steamid)
player, err := client.Player.Query().Where(player.Steamid(c.Steamid)).Only(ctx)
if ent.IsNotFound(err) {
player, err = client.Player.Create().
SetSteamid(c.Steamid).
Save(ctx)
if err != nil {
log.Log.Errorf("failed to save Player: %v", err)
return err
}
}
_, err = client.Character.Create().
SetID(c.ID).
SetPlayer(player).
SetVersion(1).
SetSlot(c.Slot).
SetSize(c.Size).
SetData(c.Data).
Save(ctx)
if err != nil {
log.Log.Errorf("failed to save Player: %v", err)
return err
failed++
errLog.WriteString(fmt.Sprintf("failed to save character! %v\n\t%+v\n", err, c))
}
}
_, err = client.Character.Create().
SetID(c.ID).
SetPlayer(player).
SetVersion(1).
SetSlot(c.Slot).
SetSize(c.Size).
SetData(c.Data).
Save(ctx)
if err != nil {
failed++
errLog.WriteString(fmt.Sprintf("failed to save character! %v\n\t%+v\n", err, c))
errLog.Close()
if failed > 0 {
log.Log.Printf("completed migration with %d errors!\n", failed)
}
log.Log.Println("Migrated all characters")

return nil
}()

if err != nil {
// Error detected, revert db changes if possible
if _, err := os.Stat(dbBakFileName); err == nil {
os.Remove(dbFileName)
os.Remove(oldDbFileName)
os.Rename(dbBakFileName, dbFileName)
}
log.Log.Fatalln("failed to migrate DB")
}
errLog.Close()
if failed > 0 {
log.Log.Printf("completed migration with %d errors!\n", failed)
}
log.Log.Println("Migrated all characters")

return nil
}()

if err != nil {
// Error detected, revert db changes if possible
if _, err := os.Stat(dbBakFileName); err == nil {
os.Remove(dbFileName)
os.Remove(oldDbFileName)
os.Rename(dbBakFileName, dbFileName)
}
log.Log.Fatalln("failed to migrate DB")

// happy path cleanup, we don't want to delete old one incase we need to revert suddenly.
os.Remove(dbBakFileName)
}

// happy path cleanup
os.Remove(dbBakFileName)
os.Remove(oldDbFileName)
}

0 comments on commit 15d666a

Please sign in to comment.