package db import ( "errors" "fmt" "io/fs" "kooltap-updateserver/internal/models" "log" "os" "github.com/glebarez/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) // ImportSQLite copies releases from the old SQLite database at path into db, // once. It does nothing when the file does not exist. After a successful // import the file is renamed to path+".imported" so it is not imported again. func ImportSQLite(db *gorm.DB, path string) error { if path == "" { return nil } if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) { return nil } else if err != nil { return err } var existing int64 if err := db.Model(&models.Release{}).Count(&existing).Error; err != nil { return err } if existing > 0 { log.Printf("skipping SQLite import from %s: database already has %d releases", path, existing) return nil } src, err := gorm.Open(sqlite.Open(path), &gorm.Config{Logger: logger.Discard}) if err != nil { return fmt.Errorf("open sqlite %s: %w", path, err) } var releases []models.Release err = src.Order("id").Find(&releases).Error // Close before renaming the file below if sqlDB, cerr := src.DB(); cerr == nil { sqlDB.Close() } if err != nil { return fmt.Errorf("read sqlite releases: %w", err) } err = db.Transaction(func(tx *gorm.DB) error { if len(releases) > 0 { if err := tx.Create(&releases).Error; err != nil { return err } } // Rows were inserted with their original IDs, so move the sequence past them if tx.Dialector.Name() == "postgres" { return tx.Exec(`SELECT setval(pg_get_serial_sequence('releases', 'id'), COALESCE((SELECT MAX(id) FROM releases), 0) + 1, false)`).Error } return nil }) if err != nil { return fmt.Errorf("import releases: %w", err) } if err := os.Rename(path, path+".imported"); err != nil { log.Printf("imported SQLite database but could not rename it: %v", err) } log.Printf("imported %d releases from %s", len(releases), path) return nil }