Skip to content

Commit 842ee52

Browse files
authored
fix decimal migrate error.(#7450) (#7450)
Signed-off-by: Chise1 <[email protected]>
1 parent 23c0d7c commit 842ee52

File tree

2 files changed

+123
-2
lines changed

2 files changed

+123
-2
lines changed

migrator/migrator.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,6 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
474474
// found, smart migrate
475475
fullDataType := strings.TrimSpace(strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL))
476476
realDataType := strings.ToLower(columnType.DatabaseTypeName())
477-
478477
var (
479478
alterColumn bool
480479
isSameType = fullDataType == realDataType
@@ -513,8 +512,19 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
513512
}
514513
}
515514
}
515+
}
516516

517-
// check precision
517+
// check precision
518+
if realDataType == "decimal" || realDataType == "numeric" &&
519+
regexp.MustCompile(realDataType+`\(.*\)`).FindString(fullDataType) != "" { // if realDataType has no precision,ignore
520+
precision, scale, ok := columnType.DecimalSize()
521+
if ok {
522+
if !strings.HasPrefix(fullDataType, fmt.Sprintf("%s(%d,%d)", realDataType, precision, scale)) &&
523+
!strings.HasPrefix(fullDataType, fmt.Sprintf("%s(%d)", realDataType, precision)) {
524+
alterColumn = true
525+
}
526+
}
527+
} else {
518528
if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision {
519529
if regexp.MustCompile(fmt.Sprintf("[^0-9]%d[^0-9]", field.Precision)).MatchString(m.DataTypeOf(field)) {
520530
alterColumn = true

tests/migrate_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,3 +1890,114 @@ func TestMigrateWithUniqueIndexAndUnique(t *testing.T) {
18901890
}
18911891
}
18921892
}
1893+
1894+
func testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string {
1895+
tracer := Tracer{
1896+
Logger: DB.Config.Logger,
1897+
Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
1898+
sql, _ := fc()
1899+
if strings.HasPrefix(sql, "ALTER TABLE ") {
1900+
t.Fatalf("shouldn't execute ALTER COLUMN TYPE if decimal is not change: sql: %s", sql)
1901+
}
1902+
},
1903+
}
1904+
session := DB.Session(&gorm.Session{Logger: tracer})
1905+
1906+
DB.Migrator().DropTable(model1)
1907+
var modifySql []string
1908+
if err := session.AutoMigrate(model1); err != nil {
1909+
t.Fatalf("failed to auto migrate, got error: %v", err)
1910+
}
1911+
if err := session.AutoMigrate(model1); err != nil {
1912+
t.Fatalf("failed to auto migrate, got error: %v", err)
1913+
}
1914+
tracer2 := Tracer{
1915+
Logger: DB.Config.Logger,
1916+
Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
1917+
sql, _ := fc()
1918+
modifySql = append(modifySql, sql)
1919+
},
1920+
}
1921+
session2 := DB.Session(&gorm.Session{Logger: tracer2})
1922+
err := session2.Table("migrate_decimal_columns").Migrator().AutoMigrate(model2)
1923+
if err != nil {
1924+
t.Fatalf("failed to get column types, got error: %v", err)
1925+
}
1926+
return modifySql
1927+
}
1928+
1929+
func decimalColumnsTest[T, T2 any](t *testing.T, expectedSql []string) {
1930+
var t1 T
1931+
var t2 T2
1932+
modSql := testAutoMigrateDecimal(t, t1, t2)
1933+
var alterSQL []string
1934+
for _, sql := range modSql {
1935+
if strings.HasPrefix(sql, "ALTER TABLE ") {
1936+
alterSQL = append(alterSQL, sql)
1937+
}
1938+
}
1939+
1940+
if len(alterSQL) != 3 {
1941+
t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL)
1942+
}
1943+
for i := range alterSQL {
1944+
if alterSQL[i] != expectedSql[i] {
1945+
t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL)
1946+
}
1947+
}
1948+
}
1949+
1950+
func TestAutoMigrateDecimal(t *testing.T) {
1951+
if DB.Dialector.Name() == "sqlserver" { // database/sql will replace numeric to decimal. so only support decimal.
1952+
type MigrateDecimalColumn struct {
1953+
RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"`
1954+
RecID2 int64 `gorm:"column:recid2;type:decimal(8);not null" json:"recid2"`
1955+
RecID3 int64 `gorm:"column:recid3;type:decimal(8,1);not null" json:"recid3"`
1956+
}
1957+
type MigrateDecimalColumn2 struct {
1958+
RecID1 int64 `gorm:"column:recid1;type:decimal(8);not null" json:"recid1"`
1959+
RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"`
1960+
RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"`
1961+
}
1962+
expectedSql := []string{
1963+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" decimal(8) NOT NULL`,
1964+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" decimal(9,1) NOT NULL`,
1965+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" decimal(9,2) NOT NULL`,
1966+
}
1967+
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
1968+
} else if DB.Dialector.Name() == "postgres" {
1969+
type MigrateDecimalColumn struct {
1970+
RecID1 int64 `gorm:"column:recid1;type:numeric(9,0);not null" json:"recid1"`
1971+
RecID2 int64 `gorm:"column:recid2;type:numeric(8);not null" json:"recid2"`
1972+
RecID3 int64 `gorm:"column:recid3;type:numeric(8,1);not null" json:"recid3"`
1973+
}
1974+
type MigrateDecimalColumn2 struct {
1975+
RecID1 int64 `gorm:"column:recid1;type:numeric(8);not null" json:"recid1"`
1976+
RecID2 int64 `gorm:"column:recid2;type:numeric(9,1);not null" json:"recid2"`
1977+
RecID3 int64 `gorm:"column:recid3;type:numeric(9,2);not null" json:"recid3"`
1978+
}
1979+
expectedSql := []string{
1980+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" TYPE numeric(8) USING "recid1"::numeric(8)`,
1981+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" TYPE numeric(9,1) USING "recid2"::numeric(9,1)`,
1982+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" TYPE numeric(9,2) USING "recid3"::numeric(9,2)`,
1983+
}
1984+
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
1985+
} else if DB.Dialector.Name() == "mysql" {
1986+
type MigrateDecimalColumn struct {
1987+
RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"`
1988+
RecID2 int64 `gorm:"column:recid2;type:decimal(8);not null" json:"recid2"`
1989+
RecID3 int64 `gorm:"column:recid3;type:decimal(8,1);not null" json:"recid3"`
1990+
}
1991+
type MigrateDecimalColumn2 struct {
1992+
RecID1 int64 `gorm:"column:recid1;type:decimal(8);not null" json:"recid1"`
1993+
RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"`
1994+
RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"`
1995+
}
1996+
expectedSql := []string{
1997+
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid1` decimal(8) NOT NULL",
1998+
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid2` decimal(9,1) NOT NULL",
1999+
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid3` decimal(9,2) NOT NULL",
2000+
}
2001+
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
2002+
}
2003+
}

0 commit comments

Comments
 (0)