go-gorm / gorm

The fantastic ORM library for Golang, aims to be developer friendly
https://gorm.io
MIT License
36.57k stars 3.91k forks source link

AutoMigrate should avoid migration if table structure not changed? #6398

Open kkocdko opened 1 year ago

kkocdko commented 1 year ago

Describe the feature

AutoMigrate is useful to migrate database but it runs migration every time even if the table structure (struct define) not changed.

Motivation

The AutoMigrate is prefixed with Auto, which should be more smart. And I found that some prime users like me just use AutoMigrate at the begining of program and image it will skip migration when possible (LOL).

Related Issues

If this feature was too complex to implement, maybe add this to docs?

jinzhu commented 1 year ago

We are trying to achieve this goal, could you create a playground that could reproduce the case?

kkocdko commented 1 year ago

My personal method to run migrate on-demand. Just fit my personal usage.

package main

import (
    "encoding/json"
    "fmt"
    "reflect"

    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "gorm.io/gorm/logger"
)

// MUST keep outputs stable
func GetDefine(v any) string {
    t := reflect.TypeOf(v)
    ret := "type " + t.Name() + " struct {\n"
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        ret += " " + field.Name + " " + field.Type.Name()
        tag := string(field.Tag)
        if tag != "" {
            ret += " `" + string(field.Tag) + "`"
        }
        ret += "\n"
    }
    ret += "}"
    return ret
}

func panicErr(v error, msg ...string) {
    if v != nil {
        if len(msg) != 0 {
            fmt.Println(msg[0])
        }
        panic(v)
    }
}

type KeyValue struct {
    Key   string
    Value []byte
}

type FooBar struct {
    Field1 string
    Field2 int
    Field3 byte
}

func initGorm() error {
    gormCfg := gorm.Config{}
    gormCfg.Logger.LogMode(logger.Info)
    dsn := "file:data.db"
    d, err := gorm.Open(sqlite.Open(dsn), &gormCfg)
    panicErr(err)

    // My personal method to run migrate on-demand
    defs := map[string]string{}
    kv := KeyValue{Key: "db_def"}
    if d.First(&kv).Error == nil {
        panicErr(json.Unmarshal(kv.Value, &defs))
    }
    for _, v := range []any{FooBar{}} {
        name, def := reflect.TypeOf(v).Name(), GetDefine(v)
        if defs[name] != def {
            panicErr(d.AutoMigrate(&v))
            defs[name] = def
            fmt.Printf("AutoMigrate: %v\n", name)
        }
    }
    kv.Value, _ = json.Marshal(defs)
    d.Save(&kv)

    return nil
}

func main() {
    initGorm()
}