fix decimal migrate error.(#7450) (#7450)

Signed-off-by: Chise1 <chise123@live.com>
This commit is contained in:
Salent Olivick 2025-06-06 10:35:01 +08:00 committed by GitHub
parent 23c0d7cf05
commit 842ee527eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 123 additions and 2 deletions

View File

@ -474,7 +474,6 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
// found, smart migrate
fullDataType := strings.TrimSpace(strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL))
realDataType := strings.ToLower(columnType.DatabaseTypeName())
var (
alterColumn bool
isSameType = fullDataType == realDataType
@ -513,8 +512,19 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
}
}
}
}
// check precision
// check precision
if realDataType == "decimal" || realDataType == "numeric" &&
regexp.MustCompile(realDataType+`\(.*\)`).FindString(fullDataType) != "" { // if realDataType has no precision,ignore
precision, scale, ok := columnType.DecimalSize()
if ok {
if !strings.HasPrefix(fullDataType, fmt.Sprintf("%s(%d,%d)", realDataType, precision, scale)) &&
!strings.HasPrefix(fullDataType, fmt.Sprintf("%s(%d)", realDataType, precision)) {
alterColumn = true
}
}
} else {
if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision {
if regexp.MustCompile(fmt.Sprintf("[^0-9]%d[^0-9]", field.Precision)).MatchString(m.DataTypeOf(field)) {
alterColumn = true

View File

@ -1890,3 +1890,114 @@ func TestMigrateWithUniqueIndexAndUnique(t *testing.T) {
}
}
}
func testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string {
tracer := Tracer{
Logger: DB.Config.Logger,
Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
sql, _ := fc()
if strings.HasPrefix(sql, "ALTER TABLE ") {
t.Fatalf("shouldn't execute ALTER COLUMN TYPE if decimal is not change: sql: %s", sql)
}
},
}
session := DB.Session(&gorm.Session{Logger: tracer})
DB.Migrator().DropTable(model1)
var modifySql []string
if err := session.AutoMigrate(model1); err != nil {
t.Fatalf("failed to auto migrate, got error: %v", err)
}
if err := session.AutoMigrate(model1); err != nil {
t.Fatalf("failed to auto migrate, got error: %v", err)
}
tracer2 := Tracer{
Logger: DB.Config.Logger,
Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
sql, _ := fc()
modifySql = append(modifySql, sql)
},
}
session2 := DB.Session(&gorm.Session{Logger: tracer2})
err := session2.Table("migrate_decimal_columns").Migrator().AutoMigrate(model2)
if err != nil {
t.Fatalf("failed to get column types, got error: %v", err)
}
return modifySql
}
func decimalColumnsTest[T, T2 any](t *testing.T, expectedSql []string) {
var t1 T
var t2 T2
modSql := testAutoMigrateDecimal(t, t1, t2)
var alterSQL []string
for _, sql := range modSql {
if strings.HasPrefix(sql, "ALTER TABLE ") {
alterSQL = append(alterSQL, sql)
}
}
if len(alterSQL) != 3 {
t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL)
}
for i := range alterSQL {
if alterSQL[i] != expectedSql[i] {
t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL)
}
}
}
func TestAutoMigrateDecimal(t *testing.T) {
if DB.Dialector.Name() == "sqlserver" { // database/sql will replace numeric to decimal. so only support decimal.
type MigrateDecimalColumn struct {
RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"`
RecID2 int64 `gorm:"column:recid2;type:decimal(8);not null" json:"recid2"`
RecID3 int64 `gorm:"column:recid3;type:decimal(8,1);not null" json:"recid3"`
}
type MigrateDecimalColumn2 struct {
RecID1 int64 `gorm:"column:recid1;type:decimal(8);not null" json:"recid1"`
RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"`
RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"`
}
expectedSql := []string{
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" decimal(8) NOT NULL`,
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" decimal(9,1) NOT NULL`,
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" decimal(9,2) NOT NULL`,
}
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
} else if DB.Dialector.Name() == "postgres" {
type MigrateDecimalColumn struct {
RecID1 int64 `gorm:"column:recid1;type:numeric(9,0);not null" json:"recid1"`
RecID2 int64 `gorm:"column:recid2;type:numeric(8);not null" json:"recid2"`
RecID3 int64 `gorm:"column:recid3;type:numeric(8,1);not null" json:"recid3"`
}
type MigrateDecimalColumn2 struct {
RecID1 int64 `gorm:"column:recid1;type:numeric(8);not null" json:"recid1"`
RecID2 int64 `gorm:"column:recid2;type:numeric(9,1);not null" json:"recid2"`
RecID3 int64 `gorm:"column:recid3;type:numeric(9,2);not null" json:"recid3"`
}
expectedSql := []string{
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" TYPE numeric(8) USING "recid1"::numeric(8)`,
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" TYPE numeric(9,1) USING "recid2"::numeric(9,1)`,
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" TYPE numeric(9,2) USING "recid3"::numeric(9,2)`,
}
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
} else if DB.Dialector.Name() == "mysql" {
type MigrateDecimalColumn struct {
RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"`
RecID2 int64 `gorm:"column:recid2;type:decimal(8);not null" json:"recid2"`
RecID3 int64 `gorm:"column:recid3;type:decimal(8,1);not null" json:"recid3"`
}
type MigrateDecimalColumn2 struct {
RecID1 int64 `gorm:"column:recid1;type:decimal(8);not null" json:"recid1"`
RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"`
RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"`
}
expectedSql := []string{
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid1` decimal(8) NOT NULL",
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid2` decimal(9,1) NOT NULL",
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid3` decimal(9,2) NOT NULL",
}
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
}
}