Files
MeBox/internal/database/sqlite_migration_bootstrap.go
T
truewhile 44ca451cd4 优化
优化
2026-08-24 17:40:43 +08:00

127 lines
3.1 KiB
Go

package database
import (
"fmt"
"strings"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"github.com/ShukeBta/MMTL/internal/model"
)
func resetBootstrapTargetBeforeSQLiteMigrationIfSafe(src, target *gorm.DB, log *zap.Logger) error {
hasRows, err := sqliteSourceHasMigratableRows(src)
if err != nil {
return err
}
if !hasRows {
return nil
}
bootstrapOnly, err := targetLooksLikeBootstrapOnly(target)
if err != nil || !bootstrapOnly {
return err
}
for i := len(model.AllModels()) - 1; i >= 0; i-- {
m := model.AllModels()[i]
if !target.Migrator().HasTable(m) {
continue
}
if err := target.Session(&gorm.Session{AllowGlobalUpdate: true}).Unscoped().Delete(m).Error; err != nil {
return fmt.Errorf("clear bootstrap target table %T: %w", m, err)
}
}
if log != nil {
log.Warn("cleared bootstrap postgres rows before sqlite migration")
}
return nil
}
func sqliteSourceHasMigratableRows(src *gorm.DB) (bool, error) {
for _, table := range []string{"users", "libraries", "media", "settings"} {
exists, err := sqliteTableExists(src, table)
if err != nil {
return false, err
}
if !exists {
continue
}
var count int64
if err := src.Raw("SELECT COUNT(1) FROM " + quoteIdent(table)).Scan(&count).Error; err != nil {
return false, fmt.Errorf("count sqlite table %s: %w", table, err)
}
if count > 0 {
return true, nil
}
}
return false, nil
}
func targetLooksLikeBootstrapOnly(target *gorm.DB) (bool, error) {
for _, m := range []any{
&model.Library{},
&model.Series{},
&model.Media{},
&model.PlaybackHistory{},
&model.Favorite{},
&model.Playlist{},
&model.PlaylistItem{},
} {
if !target.Migrator().HasTable(m) {
continue
}
var count int64
if err := target.Unscoped().Model(m).Count(&count).Error; err != nil {
return false, err
}
if count > 0 {
return false, nil
}
}
var userCount int64
if !target.Migrator().HasTable(&model.User{}) {
return true, nil
}
if err := target.Model(&model.User{}).Count(&userCount).Error; err != nil {
return false, err
}
if userCount == 0 {
return true, nil
}
if userCount != 1 {
return false, nil
}
var user model.User
if err := target.Unscoped().Where("username = ?", "admin").First(&user).Error; err != nil {
return false, nil
}
return user.Role == "admin", nil
}
func sqliteMigrationMarkedComplete(db *gorm.DB) (bool, error) {
var value string
err := db.Raw("SELECT value FROM "+quoteIdent("settings")+" WHERE "+quoteIdent("key")+" = ?", sqliteMigrationCompleteSettingKey).Scan(&value).Error
if err != nil {
return false, fmt.Errorf("check sqlite migration marker: %w", err)
}
return strings.EqualFold(strings.TrimSpace(value), "true"), nil
}
func markSQLiteMigrationComplete(db *gorm.DB) error {
now := time.Now()
if err := db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "key"}},
DoUpdates: clause.AssignmentColumns([]string{"value", "updated_at"}),
}).Create(&model.Setting{
Key: sqliteMigrationCompleteSettingKey,
Value: "true",
UpdatedAt: now,
}).Error; err != nil {
return fmt.Errorf("mark sqlite migration complete: %w", err)
}
return nil
}